We have had to deal with a similar issue. I am not sure if we did it correctly, but here is our approach:
1. Create a Global.ASAX file, and write code for the Session_End event.
2. We used a database table to track active sessions. I think that is a safer way than a static variable because if you decide to deploy your application on multiple application pools, each pool will hold a copy of the static variable, which means you can't really tell who is active on the system (each version of the application will only see its copy of the static variable).
3. We also added a 'heartbeat' timer to the main form of the application, and set it to 60 seconds. Each timer event updates the database 'ActiveSession' table with the last time each client was heard from. This catches cases where the user simply closes the browser - their last heatbeat will indicate an inactive session, which we can delete periodically. Our hearbeat timer is also used to display broadcast messages. We have found that its very useful to be able to broadcast to everyone who is active in the application (such as 'System down for maintenace at 4:00pm.. please save your work').
4. We have an explicit logoff in the application, and if the user clicks that, we delete their 'ActiveSession' record.
5. We wrote an admin screen that shows the active users in the application, and we give the administrator the option to just delete the session record manually.
Overall, this approach seems to achieve what we needed, which is a way to tell who is on the system at any time, and optionally communicate with them. We mainly use the ActiveSession to logically lock records.. so if someone is working on a particular record, other users can't update the same record.
In my testing, i have found the global.asax Session_End event is not very dependable. Not sure why.. but I don't think its solid enough to use by itself.
In reading your posting, if you are only caching user-data for performance reasons,.. you may as well put that data into a session variable since your overall memory footprint will be the same. In that case, when the session dies, it will release the userdata.
One last comment: I posted a note about this elsewhere, but I believe that VWG Server is holding onto the Mainform object and never releasing it. In my testing, i have never seen a destructor event fired for the main class, which indicates to me that they are locked in memory forever, even if the session is closed. Maybe someone else has noticed this as well. What this means is that any module-level variables that you may hold in your main form may never get released.
Well, I hope that makes sense. If there are other dependable ways to do this, we would like to hear about them.
Mitch Stephens
HRTMS