locked
SessionStateModule "End" event not firing RRS feed

  • Question

  • User-1053827082 posted

    Hi,

    I'm getting the "SessionStateModule" object of my web aplication to append code in the start and the end of sessions. I can't put my code in Global.asax because my code intend to be a generic for every web projects.

    public static void TurnOn()
    {
        var s = (SessionStateModule)HttpContext.Current.ApplicationInstance.Modules["Session"]       
        s.End += new EventHandler(s_End);
        s.Start += new EventHandler(s_Start);
    }
    
    private static void s_Start(object sender, EventArgs e)
    {
        //foo
        //bar
    }
    
    private static void s_End(object sender, EventArgs e)
    {
        //foo
        //bar
    }


    The "Start" event runs perfect, but the "End" event is not firing at the end of session timeout. What is the problem?

    Friday, August 24, 2012 4:22 PM

Answers

  • User-1053827082 posted

    Yea, I think you're out of luck (without reflection).

    Then the luck came to me now.  ;)

    public static void TurnOn()
    {
        //Session Start
        var s = (SessionStateModule)HttpContext.Current.ApplicationInstance.Modules["Session"]
        s.Start += new EventHandler(s_Start);
    
        //Session End
        var webAssembly = Assembly.GetAssembly(typeof(HttpApplication));
        var t = webAssembly.GetType("System.Web.HttpApplicationFactory", true);
        var fieldtheApplicationFactory = t.GetField("_theApplicationFactory", BindingFlags.NonPublic | BindingFlags.Static);
        var fieldEnd = t.GetField("_sessionOnEndMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        var appFactory = fieldtheApplicationFactory.GetValue(null);
                            
    
        lock (appFactory)
        {
            fieldEnd.SetValue(appFactory, typeof(not_relevant).GetMethod("s_End", BindingFlags.NonPublic | BindingFlags.Static));
        }
    }
    
    private static void s_Start(object sender, EventArgs e)
    {
        //foo
        //bar
    }
    
    private static void s_End(object sender, EventArgs e)
    {
        //To-do: Preserve the original Session_End from Global.asax    
    
        //foo
        //bar
    }

    Thanks for helping.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, August 27, 2012 8:00 AM

All replies

  • User1779161005 posted

    If Session is maintained out of proc, then the Session End event is not fired in ASP.NET.

    Friday, August 24, 2012 5:47 PM
  • User-1053827082 posted

    If Session is maintained out of proc, then the Session End event is not fired in ASP.NET.

    The Start event is running normally and i'm using the InProc mode in Web.config.

    <sessionState timeout="10" mode="InProc" />

    Friday, August 24, 2012 6:16 PM
  • User1779161005 posted

    Oh sorry -- I didn't even notice your event handlers were not in global.asax. IIRC, the 4 event handlers (App_Start, App_End, Session_Start, Session_End) are all "special" event handlers and global.asax is the only place you're allowed to have them.

    I think you're getting lucky with the Session Start event, but End is a different beast. Think about it -- Start can be associated with a HTTP request (which is where the module is involved). But End is just some background thread noticing the user hasn't been back in a while -- there's no request that triggers that.

    I recall looking at the ASP.NET source code a long time ago and recalling that those 4 special methods are specially configured by ASP.NET runtime and I've never seen anything that suggests that what you're doing is supported other than in global.asax. Doesn't mean it can't be hacked (like your session start code). :)

    Friday, August 24, 2012 7:17 PM
  • User-1053827082 posted

    the 4 event handlers (App_Start, App_End, Session_Start, Session_End) are all "special" event handlers

    Yes, I understand that these 4 events are special, mainly because they are run by being with your special name (possibly by Reflection).

    Think about it -- Start can be associated with a HTTP request (which is where the module is involved). But End is just some background thread noticing the user hasn't been back in a while -- there's no request that triggers that.

    The same way that something is capable of firing the traditional methods Session_Start and Session_End, this same mechanism is firing my event Start added, but for some reason is not firing the End.

    Please, i would like a solution to bypass this problem, i've heard that it is possible to create my own session control module implementing the IHttpModule interface to replace the default SessionStateModule, but I think it will require a lot of code to implement. Another option is inherit the HttpApplication class of the Global.asax implementing Session_Start and Session_End in their traditional way, but that is bad for my goal of leaving the generic code for various projects.

    Any suggestions?

    Friday, August 24, 2012 9:05 PM
  • User1779161005 posted

    Yea, I think you're out of luck (without reflection). I just looked at the code in reflector (HttpApplicationFactory.ReflectOnApplicationType and HttpApplicationFactory.ReflectOnMethodInfoIfItLooksLikeEventHandler) and they're using reflection to look for Session_Start on the HttpApp-derived class (the one in global.asax). You'd have to assign a MethodInfo on the private member HttpApplicationFactory_sessionOnEndMethod to overwrite/replace the existing event in global.asax.

    Saturday, August 25, 2012 12:12 AM
  • User-1053827082 posted

    Yea, I think you're out of luck (without reflection).

    Then the luck came to me now.  ;)

    public static void TurnOn()
    {
        //Session Start
        var s = (SessionStateModule)HttpContext.Current.ApplicationInstance.Modules["Session"]
        s.Start += new EventHandler(s_Start);
    
        //Session End
        var webAssembly = Assembly.GetAssembly(typeof(HttpApplication));
        var t = webAssembly.GetType("System.Web.HttpApplicationFactory", true);
        var fieldtheApplicationFactory = t.GetField("_theApplicationFactory", BindingFlags.NonPublic | BindingFlags.Static);
        var fieldEnd = t.GetField("_sessionOnEndMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        var appFactory = fieldtheApplicationFactory.GetValue(null);
                            
    
        lock (appFactory)
        {
            fieldEnd.SetValue(appFactory, typeof(not_relevant).GetMethod("s_End", BindingFlags.NonPublic | BindingFlags.Static));
        }
    }
    
    private static void s_Start(object sender, EventArgs e)
    {
        //foo
        //bar
    }
    
    private static void s_End(object sender, EventArgs e)
    {
        //To-do: Preserve the original Session_End from Global.asax    
    
        //foo
        //bar
    }

    Thanks for helping.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, August 27, 2012 8:00 AM
  • User1161838157 posted
    I tried your solution and it doesn't work for me since I don't know what is the "not_relevant" variable in your code.  Please coul'd you help me since I'm not able to get the End event working in my httpmodule even if the mode is set to InProc...

    thanks
    Thursday, January 17, 2013 11:30 AM
  • User-1053827082 posted

    The not_relevant type is the static class name where you put the s_End method.

    Friday, January 18, 2013 1:12 PM