使用属性封装 ASP.NET Session 和 ViewState 变量






4.67/5 (21投票s)
一种通用且类型安全的方式来封装 ASP.NET Session 和 ViewState 变量。
引言
在我的第一篇 CodeProject 文章中。我将展示如何封装对存储在 Session
、ViewState
或 Application
集合中的对象的访问。
背景
ASP.NET 开发非常棒。使用代码隐藏类、用户控件、数据集,并封装应用程序业务逻辑,ASP.NET 开发(几乎)变成了乐趣。
但有时您会发现自己需要编写读取和写入 Session
、ViewState
甚至 Application
集合的代码。我不知道其他开发人员是怎么想的,但我讨厌使用像这样的非类型安全集合。
这就是属性和反射可以派上用场的地方 - 这样您就不需要输入所有这些强制转换。
此示例代码依赖于使用属性标记 WebForm 的字段,然后对 Page
类进行子类化以覆盖 LoadViewState
、SaveViewState
、OnInit
和 OnUnload
行为。
声明元数据
我们需要创建一个属性,以便我们可以标记要持久化的 WebForm 字段。
[AttributeUsage(AttributeTargets.Field)]
public class PersistFieldAttribute : Attribute
{
PersistLocation loc;
string key;
public PersistFieldAttribute()
: this(PersistLocation.Nowhere, null)
{
}
public PersistFieldAttribute(PersistLocation location)
: this(location, null)
{
}
public PersistFieldAttribute(PersistLocation location, string key)
{
Location = location;
Key = key;
}
public string GetKeyFor(MemberInfo mi)
{
return (Key != null ? Key + "_" + mi.Name : mi.Name);
}
public string Key
{
get { return key; }
set { key = value; }
}
public PersistLocation Location
{
get { return loc; }
set { loc = value; }
}
public static PersistFieldAttribute GetAttribute(MemberInfo mi)
{
return (PersistFieldAttribute) Attribute.GetCustomAttribute(mi,
typeof(PersistFieldAttribute));
}
public static PersistFieldAttribute GetAttribute(MemberInfo mi,
PersistLocation forLocation)
{
PersistFieldAttribute attr = GetAttribute(mi);
return (attr != null && attr.Location == forLocation ? attr : null);
}
}
我还添加了一些帮助程序(和类型安全)的静态 GetAttribute
方法。
当然,我们还需要声明 PersistLocation
枚举
public enum PersistLocation
{
Nowhere = 0x00,
Context = 0x01,
ViewState = 0x02,
Session = 0x04,
Application = 0x08,
}
(是的,我喜欢显式声明枚举值)
重写 System.Web.UI.Page
我们需要重写这四个方法
using System;
using System.Reflection;
using System.Web.UI;
public class PageEx : Page
{
const BindingFlags FieldBindingFlags =
BindingFlags.Instance|BindingFlags.NonPublic;
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
foreach (FieldInfo fi in GetType().GetFields(FieldBindingFlags))
{
PersistFieldAttribute attr =
PersistFieldAttribute.GetAttribute(fi, PersistLocation.ViewState);
if (attr != null)
{
TrySetValue(fi, ViewState[attr.GetKeyFor(fi)]);
}
}
}
protected override object SaveViewState()
{
foreach (FieldInfo fi in GetType().GetFields(FieldBindingFlags))
{
PersistFieldAttribute attr =
PersistFieldAttribute.GetAttribute(fi, PersistLocation.ViewState);
if (attr != null)
ViewState[attr.GetKeyFor(fi)] = TryGetValue(fi);
}
return base.SaveViewState();
}
protected override void OnInit(EventArgs e)
{
foreach (FieldInfo fi in GetType().GetFields(FieldBindingFlags))
{
PersistFieldAttribute attr = PersistFieldAttribute.GetAttribute(fi);
if (attr != null)
{
switch (attr.Location)
{
case PersistLocation.Application:
TrySetValue(fi, Application[attr.GetKeyFor(fi)]);
break;
case PersistLocation.Context:
TrySetValue(fi, Context.Items[attr.GetKeyFor(fi)]);
break;
case PersistLocation.Session:
TrySetValue(fi, Session[attr.GetKeyFor(fi)]);
break;
}
}
}
base.OnInit(e);
}
protected override void OnUnload(EventArgs e)
{
base.OnUnload(e);
foreach (FieldInfo fi in GetType().GetFields(FieldBindingFlags))
{
PersistFieldAttribute attr = PersistFieldAttribute.GetAttribute(fi);
if (attr != null)
{
switch (attr.Location)
{
case PersistLocation.Application:
Application[attr.GetKeyFor(fi)] = TryGetValue(fi);
break;
case PersistLocation.Context:
Context.Items[attr.GetKeyFor(fi)] = TryGetValue(fi);
break;
case PersistLocation.Session:
Session[attr.GetKeyFor(fi)] = TryGetValue(fi);
break;
}
}
}
}
}
使用属性
这是最简单的部分
public class MyCustomPage : PageEx
{
[PersistField(Key = "custompage", Location = PersistLocation.Session)]
protected DataSet ds;
[PersistField(Location = PersistLocation.ViewState)]
protected bool isLoaded = false;
.
.
.
private void Page_Load(object sender, EventArgs e)
{
if (ds == null)
ds = new DataSet();
}
}
它是如何工作的?
当创建 Page Handler(直接从您的代码隐藏类派生的类)的新实例时,将调用其构造函数。在它运行之前,您编写的内联赋值将被执行。
我们的 OnInit
重写在构造函数和内联赋值之后运行。然后调用我们的 LoadViewState
重写。
如果您的属性不在持久集合中(例如,首次运行该页面时),则不会简单地分配它。这就是您的内联赋值派上用场的时候:指定字段的默认值。
在执行我们的加载重写后,您就可以执行您的工作了。像使用普通字段一样使用这些字段。
页面卸载时,其他重写会将字段值存储在持久集合中。您就完成了!
一些警告
- 尽量不要在内联赋值中创建对象。您最终可能会浪费初始化,因为您创建的实例最终将被存储的实例替换。
- 确保在将字段持久化到
Session
或Application
时提供密钥名称,以便避免烦人的冲突。 - 这些字段以
Key
-underscore
-Member Name
的格式保存在媒体中。
结论
我希望这篇文章对其他开发人员也能像对我一样有用。谢谢。
历史
- 2005 年 4 月 4 日
First version.