What we did is we used a combination of Session state and Application state to prevent duplicate login.
When a user logs-in his userId is retrieved from the asp.net membership database and saved in the Application state as a unique object.
Since Application state is global to the application and not specific to a user session, we can check whether the userId is already saved in Application state. If it is already saved we can notify the user and stop the login.
When the session expires (on Session_End within the Global.asax file), that Application state object for that particular user can be removed from Application state so that he can log in again after his session has expired.
Here's the code:
In Login.aspx.cs:
protected void OnLoggingIn(object sender, LoginCancelEventArgs e)
{
// Accesses the database to get the logged-in user.
MembershipUser userInfo = Membership.GetUser(LoginUser.UserName);
UserMan.UserID = userInfo.ProviderUserKey.ToString();
if (Application[UserMan.UserID] != null)
{
if (Convert.ToString(Application[UserMan.UserID]) == UserMan.UserID)
{
e.Cancel = true;
}
else
{
// Save the user id retrieved from membership database to application state.
Application[UserMan.UserID] = UserMan.UserID;
}
}
else
{
Application[UserMan.UserID] = UserMan.UserID;
}
}
And in Global.asax:
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
if (Application[UserMan.UserID] != null)
{
if (Convert.ToString(Application[UserMan.UserID]) == UserMan.UserID)
{
Application.Contents.Remove(UserMan.UserID);
}
}
}
Although this solution seems a little messy, it works quite well without too much coding and fewer database hits.