Session values lost on switching server with statesession in asp.net webform with webfarm blue/green

Jean-François Ferland 20 Reputation points
2023-04-05T13:25:27.76+00:00

Hi,

I have an asp.net webform website implementing blue/green architecture to get smooth deployment. The blue/green is a webfarm using ARR in IIS 10 and the website use ASP.NET State Service. The site blue and green are on the same server (same IIS) as the ASP.NET State Service. The web.config contains the correct configuration to get the webfarm work correctly

  • <sessionState mode="StateServer" stateConnectionString="tcpip=loopback:42424" cookieless="false" timeout="20"/>
  • MachineKey and validationkey identical

When we swap the blue/green website, the user can smoothly continue his navigation on the website (same sessionid used by the user on both website each time we swap). However, the values in the session do not follow up. They seem to be compartmentalised by each website. I finally found a possible explanation stating that in a webfarm, the website should have the same ID so the session values follow. Load-Balanced IIS 7.5 Web Server ASP.NET Session State problem - Server Fault My problem is that I can't have the 2 websites having the same ID because they are in the same IIS (same server). Is there any workaround or setting I can change to correct that behaviour?

Thank you,

  • Sry for the formatting, I can't get any carriage return to work...
Internet Information Services
ASP.NET
ASP.NET
A set of technologies in the .NET Framework for building web applications and XML web services.
3,481 questions
{count} votes

Accepted answer
  1. Yurong Dai-MSFT 2,816 Reputation points Microsoft Vendor
    2023-04-06T07:20:21.0066667+00:00

    Hi @Jean-François Ferland,

    By default, sessions cannot be shared between different applications. Due to application scope, two applications with the same session ID will not share the same session.

    Sharing sessions between two web applications is not a good idea, each web application runs in it's own process and has its boundaries, so this is not recommended. Of course, there are also some workarounds on the Internet to achieve your goal. Manually set the application name on web application startup by creating your own configuration, using the HTTP module and some reflection. You can use it as a reference, but it's not a recommendation.

    Reference link:
    Sharing Session Across Applications

    Share Session between two web sites using asp.net and state server

    Session-State Modes


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the email notification for this thread.

    Best regards,

    Yurong Dai

    1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Jean-François Ferland 20 Reputation points
    2023-05-05T15:33:19.2833333+00:00

    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.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.