I spent a while coming up with this solution, so here it is for all the world to enjoy.
So you have some object that needs to be persisted between pages, so you pop it into the Session. This object may need to have some clean up done to it when it's done being used to free up resources -- like say calling the Dispose() method or similar clean up to take care of unmanaged resources.
No problem, just wire some code to the old Session_End event in Global.asax and call Dispose(). Well, that is wrong as you probably know. When Session_End fires, you can no longer reference the Session, which means you can get a reference to your object.
So here is what I did -- I put my object in the Cache instead. The Cache is Application wide, so I key the object by the users SessionID. When I insert the object in the Cache, I set a sliding expiration that is equal to the Session timeout.
string sessionKey = HttpContext.Current.Session.SessionID;
TimeSpan sessionTimeout = TimeSpan.FromMinutes(HttpContext.Current.Session.Timeout)
CacheItemRemovedCallback onRemove = new CacheItemRemovedCallback(someClass.SomeStaticMethod);
HttpContext.Current.Cache.Insert(sessionKey, /* key */
mySessionObject, /* the object */
null, /* dependency - file, folder or other keys */
Cache.NoAbsoluteExpiration, /* Absolute Expiration */
sessionTimeout, /* Sliding expiration */
CacheItemPriority.NotRemovable, /* Priority */
onRemove /* Callback function when removed */);
The key here is the sliding expiration. The timer counts down and is reset every time the object is accessed. So when the user closes the browser or leaves the site, the timer will eventually count down and expire the object from the Cache. That's when the callback function is fired. The callback function passes the key, the object and a reason why it expired to the handler. So you can grab the object at that point and call Dispose() or what have you to do the clean up you need to do.
public static void onRemoveObjectFromCache(string key, object value, CacheItemRemovedReason r)
MyCoolObject obj (MyCoolObject) value;