65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Dynamics 访问 ASP.NET Session 数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (7投票s)

2011年5月5日

CPOL

2分钟阅读

viewsIcon

47194

通过动态对象访问会话信息,我们使用动态属性来指定会话项的名称

我们都知道,在 ASP.NET(直到包括版本 3.5),访问会话数据是通过 HttpSessionState 对象的索引器完成的。例如

// C#
Session["MyInt"] = 12;
int myInt = (int)Session["MyInt"];
' Visual Basic
Session("MyInt") = 12
Dim myInt as Integer = CInt(Session("MyInt"))

System.Dynamic 命名空间(在 .NET 4.0 中引入)使我们能够创建其成员(属性、方法等)未明确编码的对象;而是根据需要动态添加。这意味着我们现在应该能够通过动态对象访问会话信息,在这种情况下,我们使用动态属性来指定会话项的名称,而不是索引器。

我们已经可以在框架的其他地方看到这方面的例子。例如,ViewData 在 MVC 中

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

// MVC 3.0
ViewBag.MyString = "Foo";
' Visual Basic
' MVC 1.0 and 2.0
ViewData("MyString") = "Foo"

' MVC 3.0
ViewBag.MyString = "Foo"

那么我们如何用 ASP.NET 会话数据实现相同的功能呢?首先,我们需要创建一个动态对象来包装当前的 HttpSessionState 对象

// C#
public sealed class SessionBag : DynamicObject
{
    private SessionBag()
    {
    }
}
' Visual Basic
Public NotInheritable Class SessionBag
    Inherits DynamicObject

    Private Sub New()

    End Sub

End Class

请注意,该类继承自 DynamicObject。这是指定运行时动态行为的基础类。此外,构造函数设置为 private。稍后会说明原因。

接下来,我们添加一个便捷属性来访问当前的 HttpSessionState 对象

// C#
private HttpSessionStateBase Session
{
    get { return new HttpSessionStateWrapper(HttpContext.Current.Session); }
}
' Visual Basic
Private ReadOnly Property Session As HttpSessionStateBase
    Get
        Return New HttpSessionStateWrapper(HttpContext.Current.Session)
    End Get
End Property

然后,我们重写 DynamicObjectTryGetMember()TrySetMember() 方法。这些方法定义了访问动态属性时我们的动态对象应该如何表现。在这种情况下,我们希望它检索和添加项目到 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;
}
' Visual Basic
Public Overrides Function TryGetMember(binder As _
       System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean
    result = Session(binder.Name)
    Return True
End Function

Public Overrides Function TrySetMember(binder As _
       System.Dynamic.SetMemberBinder, value As Object) As Boolean
    Session(binder.Name) = value
    Return True
End Function

此外,我们可以重写 TryGetIndex()TrySetIndex() 方法,以便仍然可以使用索引位置访问我们的会话数据

// 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;
}
' Visual Basic
Public Overrides Function TryGetIndex(binder As _
       System.Dynamic.GetIndexBinder, indexes() As Object, _
       ByRef result As Object) As Boolean
    Dim index As Integer = CInt(indexes(0))
    result = Session(index)
    Return Not result Is Nothing
End Function

Public Overrides Function TrySetIndex(binder As _
       System.Dynamic.SetIndexBinder, indexes() As Object, _
       value As Object) As Boolean
    Dim index As Integer = CInt(indexes(0))
    Session(index) = value
    Return True
End Function

最后,我们添加一个 static 变量来存储当前的 SessionBag 对象,并添加一个便捷属性来访问它。这确保只创建一个 SessionBag 对象,这也是其构造函数为 private 的原因

// C#
private static readonly SessionBag sessionBag;

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

public static dynamic Current
{
    get { return sessionBag; }
}
' Visual Basic
Private Shared ReadOnly _sessionBag As SessionBag

Shared Sub New()
    _sessionBag = New SessionBag()
End Sub

Public Shared ReadOnly Property Current As Object
    Get
        Return _sessionBag
    End Get
End Property

请注意,在 C# 中,Current 属性的 return 类型是 dynamic 类型。这告诉 C# 编译器对该对象使用后期绑定,并允许动态添加成员。在 Visual Basic 中,通过将 Option Strict 设置为 Off 来实现相同的功能。

类的完整代码如下

// 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; }
    }
}
' Visual Basic
Public NotInheritable Class SessionBag
    Inherits DynamicObject

    Private Shared ReadOnly _sessionBag As SessionBag

    Shared Sub New()
        _sessionBag = New SessionBag()
    End Sub

    Private Sub New()

    End Sub

    Private ReadOnly Property Session As HttpSessionStateBase
        Get
            Return New HttpSessionStateWrapper(HttpContext.Current.Session)
        End Get
    End Property

    Public Overrides Function TryGetMember(binder As _
           System.Dynamic.GetMemberBinder, ByRef result As Object) As Boolean
        result = Session(binder.Name)
        Return True
    End Function

    Public Overrides Function TrySetMember(binder As _
           System.Dynamic.SetMemberBinder, value As Object) As Boolean
        Session(binder.Name) = value
        Return True
    End Function

    Public Overrides Function TryGetIndex(binder As _
           System.Dynamic.GetIndexBinder, indexes() As Object, _
           ByRef result As Object) As Boolean
        Dim index As Integer = CInt(indexes(0))
        result = Session(index)
        Return Not result Is Nothing
    End Function

    Public Overrides Function TrySetIndex(binder As _
           System.Dynamic.SetIndexBinder, indexes() _
           As Object, value As Object) As Boolean
        Dim index As Integer = CInt(indexes(0))
        Session(index) = value
        Return True
    End Function

    Public Shared ReadOnly Property Current As Object
        Get
            Return _sessionBag
        End Get
    End Property

End Class

一个示例

这是一个 SessionBag 类在实际应用中的示例。它是一个简单的 MVC 应用程序,它生成随机数并使用 ASP.NET 会话将当前数字和上一个数字输出到浏览器

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
<%@ 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
' 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
<%@ 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>
© . All rights reserved.