My team has developed several Line of Business applications using Silverlight. Most but not all have at least 3 if not all of the following feature requests:
- Allow users to be signed into the LOB more than once. Users like having more than 1 browser tab or even more than 1 browser type with the LOB app loaded simultaneously.
- Ability to differentiate between a user in Tab1 versus Tab2 for purposes of concurrency, etc.
- Limit the number of simultaneous sessions a specific user can have. 1 is a valid constraint.
- Automatically log out a user instance after X seconds a.k.a session time out
- Gather user information such as browser type/version, screen resolution, IP, etc
- # of concurrent distinct users logged in over time.
- # of concurrent users logged in over time total.
- Max or average simultaneous sessions by users.
All of these are good feature requests for any Line of Business Application. Thinking through it, these features share an implementation detail that requires the LOB to track each signed in user instance.
Here was the plan.
- Create a new database table called AppInstance. This table will contain a list of all the active users in the system.
- Create a new database table called ArchivedAppInstance. This table contains all previously logged in users.
- Whenever a user logs in, add a row to the AppInstance table that records the UserId, timestamp of when that user logged in and any other information your LOB might want to know.
- Whenever a user logs out, archive the #3 AppInstance row to ArchivedAppInstance table noting the logged out timestamp.
The information in these 2 new tables enable the implementation of the features above. Conceptually this is very simple. In fact, 1 through 3 above are easy. #4 it turns out, in Silverlight was not as straight forward as one might think. Why? First, there are multiple ways the application can be exited such as clicking the LOB “Log Out” button, closing the browser directly, navigating away from the LOB web page or, heaven forbid, a browser crash. Except for crashing, the Silverlight application’s Exit event is raised but if you try to perform some backend action like calling a web service it won’t go through since the thread required to make the service call itself is gone.
I worked on a team to solve this. My colleague Tammy McClellan recently documented our approach for other product owners internally and I thought I would share on my blog. In addition, Tammy created an extremely small and simple sample application with our implementation. You can find the code here.