Introduction
In my last post, Session in ASP.NET MVC, I explained how to use single session for ASP.NET MVC projects. Single session is obviously the best practice. But sometimes, we many need to use multiple sessions in our project. So, let’s check a utility class for our session management.
Background
The aim would be to make a:
- Utility class which will be able to fulfill our session based requirements like adding, removing, replacing, retrieving, etc. sessions.
- Session enlistments, so that we will be able to say at a glance what number of sessions are been used in our project, and using which keys they are been stored.
Using the Code
First, we will try to enlist each individual the session’s keys/names using a enum
. This enum
will help use in later cores.
internal enum SessionEnum
{
LogOn,
Permission,
LogInName,
LogInTime
}
Now let's see the SessionManager
which manages our session
s.
As you can see, we can make an object of this class by providing the previously discussed SessionEnum
or not. And it is important to know.
- If you want to use the methods declared inside the
#region session
, you obviously have to provide a SessionEnum
, or an error would be shown. Because they work for a particular session key only.
Rest of the methods work with some basic session management requirements like (see the public
/internal
methods):
internal class SessionManager
{
private readonly HttpSessionState _session;
private readonly SessionEnum? _sessionKey;
private string SessionName
{
get
{
if (_sessionKey == null)
{
throw new NullReferenceException("No sessionKey provided at the constructor");
}
return _sessionKey.ToString();
}
}
internal SessionManager()
{
_session = HttpContext.Current.Session;
}
internal SessionManager(SessionEnum sessionKey) : this()
{
_sessionKey = sessionKey;
}
#region Depends on SessionKey provided using constructor
internal bool DoesKeyExist()
{
if (!HasAnySessions())
{
return false;
}
bool exist = false;
for (int i = 0; i < _session.Count; i++)
{
exist = _session.Keys[i] == SessionName;
if (exist)
{
break;
}
}
return exist;
}
internal bool IsNull()
{
return _session[SessionName] == null;
}
internal void SetNull()
{
_session[SessionName] = null;
}
internal TSource Get<TSource>()
{
return (TSource) _session[SessionName];
}
internal void Add<TSource>(TSource model)
{
_session.Add(SessionName, model);
}
internal void Replace<TSource>(TSource model)
{
_session[SessionName] = model;
}
internal void Remove()
{
_session.Remove(SessionName);
}
#endregion
internal string GetSessionId()
{
return _session.SessionID;
}
internal bool HasAnySessions()
{
return _session.Count > 0;
}
internal void RemoveAll()
{
_session.RemoveAll();
}
internal void AbandonSessions()
{
_session.Abandon();
}
}
Some of the uses:
new SessionManager(SessionEnum.LogOn).Add(new UserSessionModel()
{LogInName = "scott", LogInTime = DateTime.Now});
new SessionManager(SessionEnum.LogInName).Add("scott");
new SessionManager(SessionEnum.LogInTime).Add(DateTime.Now);
string sessionId = new SessionManager().GetSessionId();
bool hasSession = new SessionManager().HasAnySessions();
bool hasLogOnKey = new SessionManager(SessionEnum.LogOn).DoesKeyExist();
bool hasPermissionKey = new SessionManager(SessionEnum.Permission).DoesKeyExist();
bool isLogOnSessionNull = new SessionManager(SessionEnum.LogOn).IsNull();
bool isPermissionSessionNull = new SessionManager(SessionEnum.Permission).IsNull();
new SessionManager(SessionEnum.LogOn).Replace(new UserSessionModel()
{ LogInName = "scottRocks", LogInTime = DateTime.Now.AddDays(-2) });
var user = new SessionManager(SessionEnum.LogOn).Get<UserSessionModel>();
new SessionManager(SessionEnum.LogOn).Remove();
user = new SessionManager(SessionEnum.LogOn).Get<UserSessionModel>();
var manager = new SessionManager();
manager.RemoveAll();
hasSession = new SessionManager().HasAnySessions();
manager.AbandonSessions();
hasSession = new SessionManager().HasAnySessions();
Make the Flexibility Little Tighter
Our above class works fine. But if you want to add some restrictions, which are important if you are working with multiple session
objects in project:
- Avoid using a key to be added, more than once.
- Get a
session
means, it needs to be present till now with specifying type. Being null
is not acceptable. - Remove a
session
means, being null
is not acceptable. - If a
session
with a specific type of object using specific key is once created, it can only hold that type of object for life cycle.
This means you know exactly what is going on with the session
s, and it's going on as we wanted it to be.
To do so, re-edit the utility class by replacing some of the methods from above, which may look like mine:
internal TSource Get<TSource>()
{
if (IsNull())
{
throw new Exception(String.Format("The session with key '{0}' is null", SessionName));
}
return (TSource) _session[SessionName];
}
internal void Add<TSource>(TSource model)
{
if (DoesKeyExist())
{
throw new Exception(String.Format("The session key '{0}' is already been used,
try using another key",
SessionName));
}
_session.Add(SessionName, model);
}
internal void Replace<TSource>(TSource model)
{
if (!DoesKeyExist())
{
throw new Exception(String.Format("The session key '{0}' is not been used yet", SessionName));
}
if (!IsNull() && (model.GetType() != _session[SessionName].GetType()))
{
throw new Exception(
String.Format("The old data type of session key '{0}'
is not matching with the new data type",
SessionName));
}
_session[SessionName] = model;
}
internal void Remove()
{
if (!DoesKeyExist())
{
throw new Exception(
String.Format("The session with the key '{0}' is already been removed,
or not used yet", SessionName));
}
_session.Remove(SessionName);
}
Limitations
- I have tested the code on ASP.NET web forms, web service, and MVC.
- If you want user session model need to be more typed, try to avoid Base-child relational models on the
replace
method because the comparison may vary at some points:
model.GetType() != _session[SessionName].GetType()
- Try to use it inside the First Server Communication Layer only, rather than at logic or data layers.
Find the Visual Studio 2010 project solution in the attachment.