zoukankan      html  css  js  c++  java
  • WebForm —— 页面状态自动加载和保存(中)

    上篇我将页面状态的自动加载和保存原理讲了一下,并作了一个简单的例子。在这里我会把上篇的例子整理一下,并提供一个基类(这里我将其定义为 BasePage 类,从 Page 类继承)处理这些事情,使得程序员从赋值、取值的繁琐操作中解脱出来。

    首先定义一个特性(Attribute)。我会将这个特性放到需要自动加载和保存的属性上,以便将这些需要处理的属性从所有的页面属性中筛选出来,做进一步处理。这个特性的定义如下:

    /// <summary>
    /// 自动保存属性. 能够实现字段或属性值的自动保存和加载. 该属性只在非静态字段或属性上才能生效. 
    /// </summary>
    /// <remarks>
    /// 自动保存属性. 在页面类的属性上面加上该属性. 可以使得该字段或属性能够自动保存和自动加载. 
    /// 但是该属性必须是可序列化的. 否则抛出异常. 该属性只在非公有字段或属性上才能生效. 
    /// </remarks>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public class AutoSaveAttribute : Attribute
    {
        /// <summary>
        /// 初始化创建一个 <see cref="AutoSaveAttribute"/> 类的实例. 使得具有该属性的类的属性具有自动保存的特性. 
        /// </summary>
        public AutoSaveAttribute() { }
    }

    然后就是重写页面生命周期的某些事件,加入我们的处理代码。处理的过程为:㈠检索当前页面类型并将其需要处理的属性筛选出来(初始化过程);㈡将筛选出来的属性做保存或赋值操作(关键点)。

    ㈠筛选需要处理的属性,将其缓存到一个静态字典中,在需要的时候再取出来。这个初始化的代码如下:

    /// <summary>
    /// 用户控件类型及自动保存属性成员缓冲字典
    /// </summary>
    protected static Dictionary<Type, MemberInfo[]> CacheDic = null;
     
    /// <summary>
    /// 获得成员列表的绑定标识.
    /// </summary>
    protected static BindingFlags Flag;
     
    /// <summary>
    /// 初始化 <see cref="BasePage"/> 类.
    /// </summary>
    static BasePage()
    {
        CacheDic = new Dictionary<Type, MemberInfo[]>();
     
        Flag = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.FlattenHierarchy;
    }
     
    /// <summary>
    /// 当前页面的类型
    /// </summary>
    protected Type CurrType = null;
    /// <summary>
    /// 初始化当前页面的缓冲字典
    /// </summary>
    protected void InitCacheDic()
    {
        // 获得当前实例类型
        CurrType = Page.GetType();
     
        MemberInfo[] mems = null;
     
        if (!CacheDic.TryGetValue(CurrType, out mems))
        {
            // 自动保存属性处理
            var list = CurrType.GetMembers(Flag)
                .Where(p => Attribute.IsDefined(p, typeof(AutoSave), false))
                .ToArray();
            CacheDic[CurrType] = list;
        }
    }

    可以看到,在调用调用初始化函数 InitCacheDic 时,系统会做两件事:缓存当前页面类型、筛选需要处理的属性。筛选属性反射操作,执行一次后不再重复。

    ㈡在赋值取值时,根据 CurrType 找到需要处理的属性,反射调用即可。这里我将属性的赋值操作放在了 OnInit 方法中,具体的代码如下:

    /// <summary>
    /// 引发 <see cref="E:System.Web.UI.Control.Init"/> 事件以对页进行初始化。
    /// </summary>
    /// <param name="e">包含事件数据的 <see cref="T:System.EventArgs"/>。</param>
    protected override void OnInit(EventArgs e)
    {
        if (Page.IsPostBack)
        {
            // 初始化当前用户控件的缓冲字典
            InitCacheDic();
     
            // 获得缓冲数据列表
            var list = GetCacheData();
     
            // 自动加载 AutoSave 属性保存的值
            int index = 0;
            foreach (MemberInfo info in CacheDic[CurrType])
            {
                if (info.MemberType == MemberTypes.Property)
                {
                    PropertyInfo pi = info as PropertyInfo;
                    object value = list[index];
                    if (value != null)
                        pi.SetValue(this, value, null);
                }
                else if (info.MemberType == MemberTypes.Field)
                {
                    FieldInfo fi = info as FieldInfo;
                    object value = list[index];
                    fi.SetValue(this, value);
                }
                index++;
            }
        }
    }

    其中 GetCacheData 方法是获得该页缓存的数据。这些缓存的数据你可以放在 Session、Database、ViewState、分布式或者其它你能想到的地方。这涉及到了下篇中的内容,这里先卖个关子,相信难不倒聪明的你!

    ㈢在赋值操作时,根据 CurrType 找到需要处理的属性,反射赋值即可。这里我将属性的保存操作放在了 SaveViewState 方法中。具体的代码如下:

    /// <summary>
    /// 在这里实现属性的自动保存。
    /// </summary>
    protected override object SaveViewState()
    {
        // 初始化当前用户控件的缓冲字典
        InitCacheDic();
     
        // 初始化要保存的属性值列表
        ArrayList list = new ArrayList(); 
        int index = 0;
        foreach (MemberInfo info in CacheDic[CurrType])
        {
            if (info.MemberType == MemberTypes.Property)
            {
                PropertyInfo pi = info as PropertyInfo;
                list[index] = pi.GetValue(this, null);
            }
            else if (info.MemberType == MemberTypes.Field)
            {
                FieldInfo fi = info as FieldInfo;
                list[index] = fi.GetValue(this);
            }
        }
     
        // 保存更改
        SaveCacheData(list);
     
        return base.SaveViewState();
    }

    其中 SaveCacheData 方法是保存该页缓存的数据,和 GetCacheData 方法的作用是相反。这里你可以尽情发挥你的想象,比如保存到 Session 中?

    这样吧,为了满足某些人的懒惰心理,我先贴上演示用的数据保存和提取方法。代码如下:

    private ArrayList GetCacheData()
    {
        return (ArrayList)Session[CurrType];
    }
     
    private void SaveCacheData(ArrayList data)
    {
        Session[CurrType] = data;
    }

    好了,你的程序应该可以运行起来了。至于取值、赋值的操作使用反射,是因为需要处理的属性个数少,不密集调用时使用 Emit 或者其它技术起不到多少加速的作用。你在乎 1 微妙的加速吗?网页的网络传输才是问题吧……

  • 相关阅读:
    iOS--推送
    iOS 多线程之GCD
    iOS多线程之NSThread
    NSUserDefaults的简单使用
    stat命令的实现-mystat
    linux pwd指令的C实现
    2019-2020-1 20175307 20175308 20175319 实验五 通讯协议设计
    2019-2020-1 20175307 20175308 20175319 实验四 外设驱动程序设计
    2019-2020-1 20175307 20175308 20175319 实验三 并发程序
    2019-2020-1 20175307 20175308 20175319 实验二 固件程序设计
  • 原文地址:https://www.cnblogs.com/lenic/p/2008976.html
Copyright © 2011-2022 走看看