zoukankan      html  css  js  c++  java
  • 二进制度序列化碰到的问题

    问题描述 

     手上有一行业门户站点,每个用户都有一个子站点(或说模板),每个子站会有一些配置数据(如: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序列化后的数据不保留程序集信息.

  • 相关阅读:
    JAVA面向对象继承 及super的学习
    JAVA 封装的学习
    submit和button的区别
    Servlet四大域对象
    转发和重定向的区别
    JSTL标签用法 详解
    JSP中EL表达式的使用
    解决idea的项目启动报404的问题
    intellij idea 创建动态web项目
    解决mysql数据库中文乱码问题
  • 原文地址:https://www.cnblogs.com/lmule/p/1798315.html
Copyright © 2011-2022 走看看