Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET
Print

Accessing ASP.NET Session Data Using Dynamics

4.89/5 (7 votes)
5 May 2011CPOL2 min read 46.7K  
Access session information via a dynamic object where we use a dynamic property to specify the name of the session item

We all know that in ASP.NET (up to and including version 3.5), accessing session data was done via an indexer on the HttpSessionState object. For example:

// C#
Session["MyInt"] = 12;
int myInt = (int)Session["MyInt"];

The System.Dynamic namespace (introduced in .NET 4.0) gives us the ability to create objects whose members (properties, methods, etc.) are not specifically coded; but instead are added dynamically as and when they are required. This means we should now be able to access session information via a dynamic object where, instead of an indexer, we use a dynamic property to specify the name of the session item.

We can already see examples of where this has been done elsewhere in the framework. Take, for example, ViewData in MVC:

// C#
// MVC 1.0 and 2.0
ViewData["MyString"] = "Foo";

// MVC 3.0
ViewBag.MyString = "Foo";

So how can we achieve the same with ASP.NET session data? Firstly, we need to create ourselves a dynamic object which is going to wrap the current HttpSessionState object:

// C#
public sealed class SessionBag : DynamicObject
{
    private SessionBag()
    {
    }
}

Note how the class inherits from DynamicObject. This is a base class for specifying dynamic behaviour at run time. Additionally, the constructor is set as private. The reason for this will become apparent later.

Next, we add a convenience property for accessing the current HttpSessionState object:

// C#
private HttpSessionStateBase Session
{
    get { return new HttpSessionStateWrapper(HttpContext.Current.Session); }
}

We then override the TryGetMember() and TrySetMember() methods of DynamicObject. These methods define how our dynamic object should behave when a dynamic property is accessed. In this case, we want it to retrieve and add items to the HttpSessionState:

// C#
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
    result = Session[binder.Name];
    return true;
}

public override bool TrySetMember(SetMemberBinder binder, object value)
{
    Session[binder.Name] = value;
    return true;
}

Additionally, we can override the TryGetIndex() and TrySetIndex() methods so our session data is still accessible using its index position:

// C#
public override bool TryGetIndex(GetIndexBinder binder, 
       object[] indexes, out object result)
{
    int index = (int)indexes[0];
    result = Session[index];
    return result != null;
}

public override bool TrySetIndex(SetIndexBinder binder, 
       object[] indexes, object value)
{
    int index = (int)indexes[0];
    Session[index] = value;
    return true;
}

Finally, we add a static variable for the current SessionBag object and a convenience property for accessing it. This ensures that only one SessionBag object is created and is why its constructor is private:

// C#
private static readonly SessionBag sessionBag;

static SessionBag()
{
    sessionBag = new SessionBag();
}

public static dynamic Current
{
    get { return sessionBag; }
}

Note that in C#, the return type of the Current property is of type dynamic. This tells the C# compiler to use late binding on that object and allow members to be added dynamically. The same thing is achieved in Visual Basic by setting Option Strict to Off.

The complete code for the class is as follows:

// C#
public sealed class SessionBag : DynamicObject
{
    private static readonly SessionBag sessionBag;

    static SessionBag()
    {
        sessionBag = new SessionBag();
    }

    private SessionBag()
    {
    }

    private HttpSessionStateBase Session
    {
        get { return new HttpSessionStateWrapper(HttpContext.Current.Session); }
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = Session[binder.Name];
        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        Session[binder.Name] = value;
        return true;
    }

    public override bool TryGetIndex(GetIndexBinder 
           binder, object[] indexes, out object result)
    {
        int index = (int)indexes[0];
        result = Session[index];
        return result != null;
    }

    public override bool TrySetIndex(SetIndexBinder binder, 
           object[] indexes, object value)
    {
        int index = (int)indexes[0];
        Session[index] = value;
        return true;
    }

    public static dynamic Current
    {
        get { return sessionBag; }
    }
}

An Example

Here is an example of our SessionBag class in action. It is a simple MVC application which generates random numbers and uses the ASP.NET session to output the current number and the last number to the browser:

C#
C#
//C#
public class RandomController : Controller
{
    private Random random;

    public RandomController()
    {
        random = new Random();
    }

    public ActionResult Index()
    {
        SessionBag.Current.LastNumber = SessionBag.Current.CurrentNumber;
        int number = random.Next();
        SessionBag.Current.CurrentNumber = number;
        return View();
    }
}
ASP.NET
ASP.NET
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>

<!DOCTYPE html>
<html>
<head runat="server">
    <title>Random</title>
</head>
<body>
    <div>
        <% using (Html.BeginForm())
           { %>
        <% if (SessionBag.Current.CurrentNumber != null)
           {%>
        Current number is
        <%: SessionBag.Current.CurrentNumber %>
        <br />
        <%} %>
        <% if (SessionBag.Current.LastNumber != null)
           {%>
        Last number was
        <%: SessionBag.Current.LastNumber %>
        <br />
        <%} %>
        <input type="submit" value="Generate" />
        <%} %>
    </div>
</body>
</html>
VB.NET
VB.NET
' Visual Basic
Public Class RandomController
    Inherits System.Web.Mvc.Controller

    Private _random As Random

    Public Sub New()
        _random = New Random()
    End Sub

    Public Function Index() As ActionResult
        SessionBag.Current.LastNumber = SessionBag.Current.CurrentNumber
        Dim number As Integer = _random.Next()
        SessionBag.Current.CurrentNumber = number
        Return View()
    End Function

End Class
ASP.NET
ASP.NET
<%@ Page Language="VB" Inherits="System.Web.Mvc.ViewPage" %>

<!DOCTYPE html>
<html>
<head runat="server">
    <title>Random</title>
</head>
<body>
    <div>
        <%: "" %>
        <% Using Html.BeginForm()
        %>
        <% If Not SessionBag.Current.CurrentNumber Is Nothing Then
        %>
        Current number is
        <%: SessionBag.Current.CurrentNumber %>
        <br />
        <%
        End If%>
        <% If Not SessionBag.Current.LastNumber Is Nothing Then
        %>
        Last number was
        <%: SessionBag.Current.LastNumber %>
        <br />
        <%
        End If%>
        <input type="submit" value="Generate" />
        <%
        End Using%>
    </div>
</body>
</html>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)