zoukankan      html  css  js  c++  java
  • 二进制度序列化碰到的问题 切实认识Asp.net2.0页面编译机制

     问题描述  

     手上有一行业门户站点,每个用户都有一个子站点(或说模板),每个子站会有一些配置数据(如:logo,横栏图片或者其它一些没想到的东西)为了编程方便以及日后扩充方便,我使用直接在.aspx文件中定义的类来保存这些数据,同样为了修改方便(编译一次站的时间已经到了让人无法接受地步),我这里使用的是单独的.aspx文件(不关联.cs文件),配置数据会被分配给定义的类,然后将类序列化,使用是则执行反序列化,(基本上也就类似那个profile的过程)

     下面是序列化代码

    public static class SerializationHelper
    {
        public static string Serialize(object data)
        {


            using (MemoryStream streamMemory = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();

                // 2. Serialize the dataset object using the binary formatter
                formatter.Serialize(streamMemory, data);

                // 3. Encrypt the binary data
                string binaryData = Convert.ToBase64String(streamMemory.GetBuffer());

                // 4. Write the data to a file
                return binaryData;
            }

        }

        public static object Deserialize(string serialData)
        {
            object data = new object();
            try
            {
                MemoryStream streamMemory;
                BinaryFormatter formatter = new BinaryFormatter();

                // 2. Read the binary data, and convert it to a string
                string cipherData = serialData;

                // 3. Decrypt the binary data
                byte[] binaryData = Convert.FromBase64String(cipherData);

                // 4. Rehydrate the dataset
                streamMemory = new MemoryStream(binaryData);
                data = formatter.Deserialize(streamMemory);
            }
            catch(Exception ex)
            {
                // data could not be deserialized
                data = null;
            }

            return data;
        }
    }

    这里使用了二进制序列化,现有如下两个文件TemplateConfig.aspx跟Index.aspx.

    TemplateConfig.aspx文件里面代码如下

    <script runat="server">

       //配置数据保持类的定义

       [Serializable]
        public class Temp_EntProperty
        {
            private string _bar;
            private string _logo;
            private System.Collections.Generic.List<SubItem> size;
            public System.Collections.Generic.List<SubItem> Size
            {
                get
                {
                    if (size == null)
                    {
                        size = new System.Collections.Generic.List<SubItem>();
                    }
                    return size;
                }

            }
            public string Bar
            {
                get { return _bar; }
                set { _bar = value; }
            }
            public string Logo
            {
                get { return _logo; }
                set { _logo = value; }
            }
        }

        [Serializable]
        public class SubItem
        {
            public SubItem(string name, int value)
            {
                _name = name;
                _value = value;
            }
            private string _name;
            private int _value;
            public string Name
            {
                get { return _name; }
                set { _name = value; }
                  
            }
            public int Value
            {
                get { return _value; }
                set { _value = value; }
            }
        }

       protected void Buttion1_Click(object sender, EventArgs e)
        {

          

             Temp_EntProperty properties = new Temp_EntProperty();

             config.SerializeData = SerializationHelper.Serialize(properties); //将序列化后的数据保存到数据库

             //数据库操作....

        }
    </script>

     页面标签省略.......

    Index.aspx文件代码如下

    需要引用TemplateConfig.aspx页

    <%@ Reference Page="~/TemplateConfig.aspx" %>

    <script runat="server">

         protected void Page_Load(object sender, EventArgs e)
        {

           ASP.templateconfig_aspx.Temp_EntProperty properties=SerializationHelper.Deserialize(serialData) as ASP.templateconfig_aspx.Temp_EnterProperty ;

           //serialData是数据库中的序列化后数据.

        }

    </script>

    页面标签省略.......

    这个过程很多时候都能正常工作(现在知道,这是3种情况中的一种)

    第一种:我先运行了TemplateConfig.aspx文件,然后再运行Index.aspx文件发现一切正常.

    第二种:我修改了一下TemplateConfig.aspx文件,接着再次运行Index.aspx文件时却出现错误.

    单步调试发现反序列化是正常的,但是在将反序列化后的object类型的实例转化为Temp_EntProperty时出问题了,

    单步调试明明显示Object类型的实例就是Temp_EntProperty,但是却不允许转化....

    第三种:有时候还会碰到反序列化错误,提示找不到程序集,而奇怪的是,TemplateConfig.aspx跟Index.aspx文件的代码一行没变动过.

    问题的解决

    当然这个3个问题不是同发现的,事实上这些问题是在站点测试的个把月中陆续发现的,为了避免这些情况,我一度把自定义类(如Temp_EntProperty等)改成Hashtable ,因为Hashtable不会出现以上问题,直到今天回过头来认真的找下原因.

    根据错误信息大致可以判断出是反序列化时程序集问题(根据第三种情况),实际上以上问题的根源是,在.aspx文件中定义的类是动态编译的,其生成的程序集保存在系统盘的.net目录中(可以使用HttpRuntime.CodegenDir找到这个目录),你每次对.aspx文件的修改多会迫使asp.net运行时再次编译这个类,生成新的程序集(以App_Web_开头),而二进制序列化跟反序列化是跟程序集相关的,即使用二进制序列化后的数据里面保留了程序集信息,而反序列化时会根据这些信息找到对应的程序集,完成反序列化工作,另一方面Asp.net页面编译模型中,当页面改变后,运行时生成新的页面程序集,会有一种机制来删除旧的页面程序集.

    拿TemplateConfig.aspx页面为例, 当第一次运行时,系统在临时目录中生成其对应 App_Web_2342usaj(注意文件扩展名是.dll,这里省略,下同) 程序集,接着你修改了这个TemplateConfig.aspx页面(打个空格什么的保存下就好),你第二次请求时,运行时又会生成App_Web_asdfasd程序集,并且运行时试图删除App_Web_2342usaj程序集,但是不幸的是App_Web2342usaj程序集中已经被架载到AppDomain中了,Asp.net运行时无法删除已经架载到AppDomain中的程序集,除非你重新启动应用程序(修该下web.config或global.asax,重新启动IIS或应用程序池,当然重启电脑也可以), 在无法删除的情况下,Asp.net运行时会在临时目录下添加一个名为App_Web_2342usaj.dll.delete的空文件,这是一个标记,在应用程序重新启动时,AppDomain会被重新加载,而在加载之前,App_Web_2342usaj.dll文件会被删除,另外当你连续15次修改TemplateConfig.aspx文件后,应用程序也会重新启动,不然AppDomain中就会有太多程序集而消耗内存(参考Asp.net2.0高级编程),另外关于在页面中使用Reference后,在观察Index.aspx对应的.compiled文件(临时目录下),可以发现在index.aspx.2342s.compiled(中间数字或字符是随机)的<filedeps>节中有对文件TemplateConfig.aspx的引用,这个说明在修改了TemplateConfig.aspx文件后或触发index.aspx依赖缓存清除,也就是index.aspx会随之重新编译生成新的程序集.上面这些过程你也可以自己观察到.

     好了有了上面这些知识我们现在可以解释前面的三种情况了

     第二种情况,我们修改了一下TemplateConfig.aspx文件后接着运行Index.aspx文件,由于Index.aspx依赖于TemplateConfig.aspx文件,因此TemplateConfig.aspx文件跟Index.aspx文件都会被重新编译生成新的程序集,问题在于 index.aspx 代码中

         protected void Page_Load(object sender, EventArgs e)
        {

           ASP.templateconfig_aspx.Temp_EntProperty properties=SerializationHelper.Deserialize(serialData) as ASP.templateconfig_aspx.Temp_EnterProperty ;

           //serialData是数据库中的序列化后数据.

        }

    使用的serialData是原先Temp_EntProperty程序集序列化的数据,因此在还原时由于老的程序集也还在AppDomain中,故反序列化是成功的,但是转化却失败了,原因是两个类是同名类,但是位于不同的程序集中,如果我们这个时候重新启动下应用程序,那么就会出现第三种情况。

    第三种情况:在应用程序重新启动后,老的程序集会被删除,而数据库里保存的恰是旧程序集中Temp_EntProperty类的序列化后的数据(其中含旧程序集名称),应次在你进行反序列化时无疑会碰到程序集无法找到问题.

     问题找到了,那么如何解决呢,其实也很简单,如果你需要二进制序列化带来的高性能跟数据量少的优势,那么就把需要序列化的类放到固定的程序集中(独立建立一个项目), 如果你想在.aspx页面中直接定义需要序列化的类,那么你只能使用xml序列化,因为xml序列化后的数据不保留程序集信息.

  • 相关阅读:
    买房的贷款时间是否是越长越好?https://www.zhihu.com/question/20842791
    asp.net cookie and session
    leelazero and google colab
    download file by python in google colab
    physical processor, core, logical processor
    通过powershell操作eventlog
    openxml in sql server
    get the page name from url
    How to Execute Page_Load() in Page's Base Class?
    Difference between HttpContext.Request and Request
  • 原文地址:https://www.cnblogs.com/wdfrog/p/1268495.html
Copyright © 2011-2022 走看看