We had a problem recently with an ASP.NET website where the users' sessions were behaving oddly - session data appearing, disappearing and re-appearing.
As the site is hosted on two webservers using network load-balancing, I figured the problem must have something to do with that, but I was puzzled because I thought my configuration's use of a single, central, standard ASP.NET SQL Session State database would remove such problems.
Well, apparently this is only true if IIS is set up correctly and consistently on all your webserver machines. You see, if you check the AspStateTempApplications table in the AspState database, you'll see reference to website Instance IDs (e.g. /W3SVC/1/Root, /W3SVC/2/Root etc). So, ASP.NET hooks up your browser's session cookie to your session data using the Instance ID of the website serving you.
But what if the load-balanced servers host several sites, and due to the order of setup, your site on one server has a different Instance ID to the one on the other? That's right, you end up getting served up with different session data depending which server the load-balancer allocates to your HTTP request.
So, how to fix the prob? You just need to get the IIS Instance IDs in sync on the 2 servers. For IIS 6 there's a script you can run.
For IIS 7.5 however, you can't use adsutil.vbs style solutions anymore. However that's because the Metabase is old-hat and we rock config files now baby! You can set the id of a site using its id attribute found in C:\Windows\System32\inetsrv\config\applicationHost.config. You need admin privs to edit that, and need to restart IIS afterwards.
Easier still,
1. Remote Desktop onto server 1, open IIS Mgr, click on the problem Site and choose Advanced Settings in the sidebar.
2. Change the ID to something unique e.g. 10. Click OK.
3. Restart the web service (c:\windows\system32\iisreset /restart)
Do the same for server 2 (ensure the Site ID is the same as on server 1).
Again, restart the website after the change for it to take effect.
DaddyCode Team Blog
C# , ASP.NET, MVC, SQL, Sharepoint, JQuery and nowt else from a Web Developer in Leeds
13 Jul 2011
28 Jun 2011
Settings.settings vs web.config file with a web service reference
I have a class library called myClassLibrary that contains a reference to a web service.
The web service URL is stored in a Settings.settings file.
This in turn creates an app.config file.
I use myClassLibrary in a web project, and I ran into the problem that the default URL for the web service that I had specified in Settings.settings needed to be different when my web project was in our test environment. So I wanted to be able to override the Settings.settings value with, say, a value in my web.config.
This is how you do it:
The URL is defined as a value with Scope set to "Application" and a GenerateDefaultValueInCode property set to "TRUE".
This provides a default value when myClassLibrary is run in a non-web context with no override in the app.config file.
The URL is set in an applicationSettings block to override the default value when the class library is used in a non-web context.
Here is where we do the override for use of the class library in a web context.
Firstly we have to define a configuration block that will match the one in app.config:
Then we just implement that block with our override value.
The web service URL is stored in a Settings.settings file.
This in turn creates an app.config file.
I use myClassLibrary in a web project, and I ran into the problem that the default URL for the web service that I had specified in Settings.settings needed to be different when my web project was in our test environment. So I wanted to be able to override the Settings.settings value with, say, a value in my web.config.
This is how you do it:
Settings.settings
The URL is defined as a value with Scope set to "Application" and a GenerateDefaultValueInCode property set to "TRUE".
This provides a default value when myClassLibrary is run in a non-web context with no override in the app.config file.
app.config
The URL is set in an applicationSettings block to override the default value when the class library is used in a non-web context.
<applicationSettings>
<myClassLibrary.Settings>
<setting name="myWebServiceUrl" serializeAs="String">
<value>http://whatever.com/myService</value>
</setting>
</myClassLibrary.Settings1>
</applicationSettings>
web.config
Here is where we do the override for use of the class library in a web context.
Firstly we have to define a configuration block that will match the one in app.config:
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup" >
<section name="myClassLibrary.Settings" type="System.Configuration.ClientSettingsSection" />
</sectionGroup>
</configSections>
Then we just implement that block with our override value.
<applicationSettings>
<myClassLibrary.Settings>
<setting name="myWebServiceUrl" serializeAs="String">
<value>http://another.com/myAlternativeService</value>
</setting>
</myClassLibrary.Settings>
</applicationSettings>
22 Jun 2011
.NET Project File - Include External CommonAssemblyInfo file
I wanted to register a common AssemblyInfo.cs file in the Properties folder of my project. This file is actually external to my project, so how to do it?
Add this to your csproj file in the <ItemGroup> section with the Compile elements.
Then just strip out the [assembly: Whatever()] bits from the original AssemblyInfo.cs file and put them in the CommonAssemblyInfo.cs file.
Add this to your csproj file in the <ItemGroup> section with the Compile elements.
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>Properties\CommonAssemblyInfo.cs</Link>
</Compile>
Then just strip out the [assembly: Whatever()] bits from the original AssemblyInfo.cs file and put them in the CommonAssemblyInfo.cs file.
5 May 2011
.Net DateTime with TimeZones , UTC, DST problems
Doing DateTime work with different TimeZones? Having a pain converting to UTC, dealing with Daylight Saving Time (DST) etc?
Check the TimeZoneInfo class in .Net 3.5 +.
Check the TimeZoneInfo class in .Net 3.5 +.
8 Apr 2011
Localization of jQuery UI DatePicker with ASP.NET
Hello folks! Do you want to make your jQuery DatePicker play nicely with your ASP.NET localization settings?
When you set up your DatePicker inputs on the page, try this:
When you set up your DatePicker inputs on the page, try this:
$('.DatePicker').datepicker({
dayNamesMin: ['<%= string.Join("', '", System.Globalization.DateTimeFormatInfo.CurrentInfo.ShortestDayNames) %>'],
monthNames: ['<%= string.Join("', '", System.Globalization.DateTimeFormatInfo.CurrentInfo.MonthNames) %>'],
monthNamesShort: ['<%= string.Join("', '", System.Globalization.DateTimeFormatInfo.CurrentInfo.AbbreviatedMonthNames) %>']
});
6 Apr 2011
DevExpress ASP.NET Chart : Trial message problem
Well this was annoying and wasted half a day!
We have a registered copy of DevExpress which we use on a site for charting.
We deployed our ASP.NET site to the live server.
We deployed the registered DevExpress DLLs to the site bin folder.
(Find them in C:\Program Files (x86)\DevExpress 2010.1\Components\Sources\DevExpress.DLL - Don't copy the *.design.dll files though)
The web chart pages worked but displayed a message about using the TRIAL version of DevExpress.
Turns out that VS2010 was creating a licenses.licx file in the root of the project which was screwing it up. Annoyingly, the licx file didn't show in the Solution Explorer until you click the little "Show All Files" icon.
Solution: We had to delete the licenses.licx file, then rebuild the project, then redeploy.
More info: http://community.devexpress.com/blogs/ctodx/archive/2009/03/06/licenses-licx-file-woes.aspx
We have a registered copy of DevExpress which we use on a site for charting.
We deployed our ASP.NET site to the live server.
We deployed the registered DevExpress DLLs to the site bin folder.
(Find them in C:\Program Files (x86)\DevExpress 2010.1\Components\Sources\DevExpress.DLL - Don't copy the *.design.dll files though)
The web chart pages worked but displayed a message about using the TRIAL version of DevExpress.
Turns out that VS2010 was creating a licenses.licx file in the root of the project which was screwing it up. Annoyingly, the licx file didn't show in the Solution Explorer until you click the little "Show All Files" icon.
Solution: We had to delete the licenses.licx file, then rebuild the project, then redeploy.
More info: http://community.devexpress.com/blogs/ctodx/archive/2009/03/06/licenses-licx-file-woes.aspx
18 Feb 2011
Free Reflector Alternative
RedGate! Bah, and humbug. Their DB products have often come in useful for me, but they are also pretty slow and I can't help thinking that they could be a lot better. But now they've riled me! Raaargh!
Reflector's forced-update feature was a bit annoying even back in the days of Lutz Roeder, but at least there was a fair idea behind it - to ensure everyone had the latest and greatest free version. Now they've revealed that the forced update from the free V6 version is going to install the paid-for V7 version with an evaluation period before lock-down.
Well, this sucks. Why can't they release a final free V6 version with no forced update? Oh yeah, $$$.
Good job SharpDevelop have released their own free alternative to Reflector. I've had a play and even in Beta it does everything I need. So wooyay to them!
Reflector's forced-update feature was a bit annoying even back in the days of Lutz Roeder, but at least there was a fair idea behind it - to ensure everyone had the latest and greatest free version. Now they've revealed that the forced update from the free V6 version is going to install the paid-for V7 version with an evaluation period before lock-down.
Well, this sucks. Why can't they release a final free V6 version with no forced update? Oh yeah, $$$.
Good job SharpDevelop have released their own free alternative to Reflector. I've had a play and even in Beta it does everything I need. So wooyay to them!
24 Dec 2010
ASP.NET MVC 2 Freelance Contractor in Leeds, Yorkshire
I'm a Freelance C# / ASP.NET web application developer based in Leeds, Yorkshire, UK. I've years of experience and have just finished a funky AJAX-heavy MVC contract with oodles of Entity Framework fun, and my usual attention to User Experience. Do you want to hire me? In January I'll be free to do another contract so please get in touch if you think I could help!
17 Dec 2010
Lenovo T410 2-finger scroll edge problem
I love my new Lenovo T410, but the 2-finger scrolling didn't seem to be working right. The area of the touchpad in which the scroll would be detected seemed too narrow. Turns out that "gesture filtering" was enabled and needed to be turned off.
Go to mouse properties / ultranav / touchpad / settings / Smart Check Settings / Gesture Filtering
15 Dec 2010
Castle ActiveRecord / NHibernate Update Event Interceptor to Log Dirty Property Changes
Man I love the Castle Project documentation. Or at least, I would if I was a robot with a brain the size of a planet and an unlimited amount of time on my hands. Sadly I'm a human and find the whole thing impenetrable.
All I wanted to do was to intercept the NHibernate entity update event so that I could log the changes made. With kudos to this dude and this dude, here's how I did it:
1. In my project create a new class:
Note it uses a struct called EntityPropertyUpdate and a collection called RuntimeManager.EntityPropertyUpdates to keep a log of the changes. You'll have to roll your own if you want to do what I'm doing here, otherwise just modify the code as you see fit.
The other key thing is the ActiveRecordStarter.Initialize() call, which had to be changed to include the assembly containing the AuditUpdateListener class, which happened to be the main executing assembly. I was doing my AR Initialisation in global.asax so it now looks like this:
That's all!
Incidentally though, if you want to use an ambient AR SessionScope like I do, add this to global.asax:
All I wanted to do was to intercept the NHibernate entity update event so that I could log the changes made. With kudos to this dude and this dude, here's how I did it:
1. In my project create a new class:
using Castle;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Attributes;
using NHibernate;
using NHibernate.Event;
Note it uses a struct called EntityPropertyUpdate and a collection called RuntimeManager.EntityPropertyUpdates to keep a log of the changes. You'll have to roll your own if you want to do what I'm doing here, otherwise just modify the code as you see fit.
[EventListener()]
public class AuditUpdateListener : IPostUpdateEventListener
{
private const string _noValueString = "EMPTY";
private static string getStringValueFromStateArray(object[] stateArray, int position)
{
var value = stateArray[position];
return value == null || value.ToString() == string.Empty
? _noValueString
: value.ToString();
}
public void OnPostUpdate(PostUpdateEvent evt)
{
if (!(evt.Entity is Log_G))
{
var dirtyFieldIndexes = evt.Persister.FindDirty(evt.State, evt.OldState, evt.Entity, evt.Session);
foreach (var dirtyFieldIndex in dirtyFieldIndexes)
{
var oldValue = getStringValueFromStateArray(evt.OldState, dirtyFieldIndex);
var newValue = getStringValueFromStateArray(evt.State, dirtyFieldIndex);
if (oldValue != newValue)
RuntimeManager.EntityPropertyUpdates.Add(new RuntimeManager.EntityPropertyUpdate { EntityName = evt.Entity.GetType().Name, PropertyName = evt.Persister.PropertyNames[dirtyFieldIndex], PropertyFrom = oldValue, PropertyTo = newValue });
}
}
}
}
The other key thing is the ActiveRecordStarter.Initialize() call, which had to be changed to include the assembly containing the AuditUpdateListener class, which happened to be the main executing assembly. I was doing my AR Initialisation in global.asax so it now looks like this:
protected void Application_Start()
{
// ActiveRecord Setup
IConfigurationSource source = ActiveRecordSectionHandler.Instance;
Assembly asm = Assembly.Load("MyApp.DataLayer");
ActiveRecordStarter.Initialize(new Assembly[] {asm, Assembly.GetExecutingAssembly()}, source, new Type[] { });
}
That's all!
Incidentally though, if you want to use an ambient AR SessionScope like I do, add this to global.asax:
protected void Application_BeginRequest(Object sender, EventArgs e)
{
// Create a SessionScope for ActiveRecord
HttpContext.Current.Items.Add("ar.sessionscope", new SessionScope(FlushAction.Never));
}
protected void Application_EndRequest(object sender, EventArgs e)
{
// Close the SessionScope for ActiveRecord
try
{
SessionScope scope = HttpContext.Current.Items["ar.sessionscope"] as SessionScope;
if (scope != null)
scope.Dispose();
}
catch (Exception ex)
{
HttpContext.Current.Trace.Warn("Error", "EndRequest: " + ex.Message, ex);
}
}
Subscribe to:
Posts (Atom)
If I helped you out today, you can buy me a beer below. Cheers!