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

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (21投票s)

2005年4月13日

CPOL

2分钟阅读

viewsIcon

96770

downloadIcon

412

一种通用且类型安全的方式来封装 ASP.NET Session 和 ViewState 变量。

引言

在我的第一篇 CodeProject 文章中。我将展示如何封装对存储在 SessionViewStateApplication 集合中的对象的访问。

背景

ASP.NET 开发非常棒。使用代码隐藏类、用户控件、数据集,并封装应用程序业务逻辑,ASP.NET 开发(几乎)变成了乐趣。

但有时您会发现自己需要编写读取和写入 SessionViewState 甚至 Application 集合的代码。我不知道其他开发人员是怎么想的,但我讨厌使用像这样的非类型安全集合。

这就是属性和反射可以派上用场的地方 - 这样您就不需要输入所有这些强制转换。

此示例代码依赖于使用属性标记 WebForm 的字段,然后对 Page 类进行子类化以覆盖 LoadViewStateSaveViewStateOnInitOnUnload 行为。

声明元数据

我们需要创建一个属性,以便我们可以标记要持久化的 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 重写。

如果您的属性不在持久集合中(例如,首次运行该页面时),则不会简单地分配它。这就是您的内联赋值派上用场的时候:指定字段的默认值。

在执行我们的加载重写后,您就可以执行您的工作了。像使用普通字段一样使用这些字段。

页面卸载时,其他重写会将字段值存储在持久集合中。您就完成了!

一些警告

  • 尽量不要在内联赋值中创建对象。您最终可能会浪费初始化,因为您创建的实例最终将被存储的实例替换。
  • 确保在将字段持久化到 SessionApplication 时提供密钥名称,以便避免烦人的冲突。
  • 这些字段以 Key - underscore - Member Name 的格式保存在媒体中。

结论

我希望这篇文章对其他开发人员也能像对我一样有用。谢谢。

历史

  • 2005 年 4 月 4 日

    First version.

© . All rights reserved.