This is my final version of the "hack" that is working great in production. I didn't have to add the "httpCookies domain" setting to my web.config to make it work (in our lagacy application, it was causing problems...).
The following code must be place in global.asax.cs
/// <summary>
/// Variable used to initialize the execution of code using reflection to correctly operate session
/// variables once (or a few times due to concurrency). The variable is static so its scope is for
/// the current application loaded in memory. When loading a new instance of the site (blue/green),
/// a new “instance” of the static variable is in memory and therefore the application will go
/// through reflection again to correctly initialize the session.
/// </summary>
private static bool hasInitializedApplicationName = false;
/// <summary>
/// This is a hack so that the values in the session variable are shared between the web sites
/// of a server farm. There is a configuration in the web.config called ApplicationName which
/// will be injected into the module that isolates session values (different if classic/integrated mode).
/// So, the first user(s) will modify this value in the module that is loaded so that sessions are
/// shared correctly for everyone.
/// *The value that is overwritten corresponds to a combination of the physical path of the website
/// and the site ID in IIS. For this to work without a hack, the second website must be on another
/// server with the same path AND the website must have the same ID. Quite fragile…”
/// </summary>
private void InitializeASPNETSession()
{
if (hasInitializedApplicationName)
return;
try
{
// Get the app name from config file...
string appName = ConfigurationManager.AppSettings["ApplicationName"];
if (string.IsNullOrEmpty(appName))
return;
foreach (string moduleName in this.Modules)
{
IHttpModule module = this.Modules[moduleName];
SessionStateModule ssm = module as SessionStateModule;
if (ssm == null)
continue;
FieldInfo storeInfo = typeof(SessionStateModule).GetField("_store", BindingFlags.Instance | BindingFlags.NonPublic);
SessionStateStoreProviderBase store = (SessionStateStoreProviderBase)storeInfo.GetValue(ssm);
if (store == null) //In IIS7 Integrated mode, module.Init() is called later
{
FieldInfo runtimeInfo = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.Static | BindingFlags.NonPublic);
HttpRuntime theRuntime = (HttpRuntime)runtimeInfo.GetValue(null);
FieldInfo appNameInfo = typeof(HttpRuntime).GetField("_appDomainAppId", BindingFlags.Instance | BindingFlags.NonPublic);
appNameInfo.SetValue(theRuntime, appName);
hasInitializedApplicationName = true; //We change the static variable here so the next calls avoid doing reflection. By the nature of the requests, it's possible that the code get exectuted multiple time but it's acceptable and will not cause side effect.
break;
}
else //IIS Classic and localhost debug IISExpress
{
Type storeType = store.GetType();
if (storeType.Name.Equals("OutOfProcSessionStateStore"))
{
FieldInfo uribaseInfo = storeType.GetField("s_uribase", BindingFlags.Static | BindingFlags.NonPublic);
uribaseInfo.SetValue(storeType, appName);
}
hasInitializedApplicationName = true; //We change the static variable here so the next calls avoid doing reflection. By the nature of the requests, it's possible that the code get exectuted multiple time but it's acceptable and will not cause side effect.
break;
}
}
}
catch (Exception ex)
{
//Log the error
}
}
public override void Init()
{
base.Init();
InitializeASPNETSession();
}
You need to add the following setting in your web.config under appSettings to make everything work together
<appSettings>
<add key="ApplicationName" value="WEBSITE_NAME" />
That's it. I hope it help and save precious time to someone else.