Today I had some strange experience.  I have been hunting a strange bug that caused something to be placed in the ASP.NET session at a time that it was not supposed to be.  My biggest problem was finding out who was putting an object in the session and what that object actually contained.

I already knew that the HttpSessionState instance was being stored in the current HttpContext's Items collection (thanks to Mr. Lutz Roeder, of course), and my initial plan was to inherit from HttpSessionState and intercept the calls made to methods like Add, Remove, etc.  Alas, HttpSessionState is marked as sealed, and none of its methods is virtual.

And then I found it!  Every HttpSessionState object takes a storage parameter on its creation -- an IHttpSessionState implementor.  All I had to to was create an object that implements this interface, and pass it to a new HttpSessionState instance.  My interface implementation simply delegates everything to the real HttpSessionState instance.  Here is how to replace the built-in session object.  Note that we need some reflection to call the internal constructor:

protected override void OnPreInit(EventArgs e)
{
    HttpSessionState session = HttpContext.Current.Session;
    Type[] parameters = new Type[] { typeof(IHttpSessionState) };
    ConstructorInfo constructor = session.GetType().GetConstructor
         (BindingFlags.Instance | BindingFlags.NonPublic, null, parameters, null);

    object[] paramValues = new object[] { new FakeSessionState(session) };
    HttpSessionState fakeSession = (HttpSessionState)constructor.Invoke(paramValues);

    HttpContext.Current.Items["AspSession"] = fakeSession;

    base.OnPreInit(e);
}

I replace the session object for my page only.  It should be possible to do that for the entire application with an HttpModule or something, but all I needed was troubleshooting a single page.

Here is my FakeSessionState class.  I can set breakpoints in its methods, debug it, log method calls, etc:

public class FakeSessionState : IHttpSessionState
{
    HttpSessionState _realSession;
    public FakeSessionState(HttpSessionState realSession)
    {
        _realSession = realSession;
    }

    #region IHttpSessionState Members

    public void Abandon()
    {
        _realSession.Abandon();
    }

    public void Add(string name, object value)
    {
        _realSession.Add(name, value);
    }

    public void Clear()
    {
        _realSession.Clear();
    }

    public int CodePage
    {
        get
        {
            return _realSession.CodePage;
        }
        set
        {
            _realSession.CodePage = value;
        }
    }

    public HttpCookieMode CookieMode
    {
        get
        {
            return _realSession.CookieMode;
        }
    }

    public void CopyTo(Array array, int index)
    {
        _realSession.CopyTo(array, index);
    }

    public int Count
    {
        get
        {
            return _realSession.Count;
        }
    }

    public System.Collections.IEnumerator GetEnumerator()
    {
        return _realSession.GetEnumerator();
    }

    public bool IsCookieless
    {
        get
        {
            return _realSession.IsCookieless;
        }
    }

    public bool IsNewSession
    {
        get
        {
            return _realSession.IsNewSession;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return _realSession.IsReadOnly;
        }
    }

    public bool IsSynchronized
    {
        get
        {
            return _realSession.IsSynchronized;
        }
    }

    public System.Collections.Specialized.NameObjectCollectionBase.KeysCollection Keys
    {
        get
        {
            return _realSession.Keys;
        }
    }

    public int LCID
    {
        get
        {
            return _realSession.LCID;
        }
        set
        {
            _realSession.LCID = value;
        }
    }

    public SessionStateMode Mode
    {
        get
        {
            return _realSession.Mode;
        }
    }

    public void Remove(string name)
    {
        _realSession.Remove(name);
    }

    public void RemoveAll()
    {
        _realSession.RemoveAll();
    }

    public void RemoveAt(int index)
    {
        _realSession.RemoveAt(index);
    }

    public string SessionID
    {
        get
        {
            return _realSession.SessionID;
        }
    }

    public HttpStaticObjectsCollection StaticObjects
    {
        get
        {
            return _realSession.StaticObjects;
        }
    }

    public object SyncRoot
    {
        get
        {
            return _realSession.SyncRoot;
        }
    }

    public int Timeout
    {
        get
        {
            return _realSession.Timeout;
        }
        set
        {
            _realSession.Timeout = value;
        }
    }

    public object this[int index]
    {
        get
        {
            return _realSession[index];
        }
        set
        {
            _realSession[index] = value;
        }
    }

    public object this[string name]
    {
        get
        {
            return _realSession[name];
        }
        set
        {
            _realSession[name] = value;
        }
    }

    #endregion
}

That is all.  Happy bug hunting!

rahnev
About the Author

Stefan Rahnev

Stefan Rahnev (@StDiR) is Product Manager for Telerik Kendo UI living in Sofia, Bulgaria. He has been working for the company since 2005, when he started out as a regular support officer. His next steps at Telerik took him through the positions of Technical Support Director, co-team leader in one of the ASP.NET AJAX teams and unit manager for UI for ASP.NET AJAX and Kendo UI. Stefan’s main interests are web development, agile processes planning and management, client services and psychology.

Related Posts

Comments