使用 Dynamics 访问 ASP.NET Session 数据






4.89/5 (7投票s)
通过动态对象访问会话信息,我们使用动态属性来指定会话项的名称
我们都知道,在 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
然后,我们重写 DynamicObject
的 TryGetMember()
和 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#
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();
}
}
<%@ 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>
' 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
<%@ 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>