zoukankan      html  css  js  c++  java
  • Ilungasoft Framework: 由Emit带来的序列化问题——IEntity的序列化支持范例[源码][04/08修正]

    本文主要探讨关于Ilungasoft Framework中动态Entity的序列化问题。如果您之前也关注过Ilungasoft Framework,您一定会被其Entity定义格式的简单所吸引,用户只需要定义实体类的接口(框架提供生成工具)。不过,这样的使用上的简洁性,也就带来了实体类序列化时的部分限制。这里,Teddy将基于一个新的Sample,客观地讨论,使用.Net框架提供的默认序列化类序列化基于本框架的Entity的多种情形。关于Ilungasoft Framework的更多文章索引及版本更新,您可以访问:Ilungasoft Framework官方首页

    更正:本文最初的论述有一些错误,在双鱼座的指点下,只需将emit动态生成的程序集加载到AppDomain就能使Sample3中所有的序列化和反序列化正确运行!请注意下文中划去的文字和红色的更正文字。

    下载

    首先,您可以从这里下载最新版本的Ilungasoft Framework和全部示例的源码。本文所谈论的范例位于您下载的压缩包内的dist/Sample3目录。
    最新的版本号为:v1.2 build 0407
    Update:
    1) Add a new Web Serial Number Validator Web Custom Control;
    2) Add an Entity Serialize Helper Class SerializeHelper under Framework.Common;
    3) Fix several little bugs.

    示例

    在列举示例代码之前,先列举在最新的版本中新增的一个类SerializeHelper.cs的代码,该类封装简化了常用的序列化方法,并且将在示例中被反复调用。

    SerializeHelper.cs

    SerializeHelper Code

    以上代码,比较简单,主要就是封装了XmlSerializer和SoapFormatter这两个类,最常用的操作,下面会结合示例进行说明。

    下面就是我们的示例的代码:

    Entities.cs

     1using System;
     2using System.Data;
     3using System.Configuration;
     4using System.Web;
     5using System.Web.Security;
     6using System.Web.UI;
     7using System.Web.UI.WebControls;
     8using System.Web.UI.WebControls.WebParts;
     9using System.Web.UI.HtmlControls;
    10using System.Xml.Serialization;
    11using System.Runtime.Serialization;
    12using Ilungasoft.Framework.Common;
    13using System.Security.Permissions;
    14
    15public interface Group : IEntity
    16{
    17    int ID getset; }
    18    string Title getset; }
    19    string Description getset; }
    20    System.DateTime CreateTime getset; }
    21    int ParentID getset; }
    22}

    23
    24public interface User : IEntity
    25{
    26    int ID getset; }
    27    string Name getset; }
    28    bool Gender getset; }
    29    double Salary getset; }
    30}

    31
    32[Serializable]
    33public class UserAndGroup
    34{
    35    public User SomeUser;
    36    public Group[] SomeGroups;
    37}

    38
    39[Serializable]
    40public class UserAndGroup2 : ISerializable
    41{
    42    public User SomeUser;
    43    public Group[] SomeGroups;
    44
    45    public UserAndGroup2()
    46    {
    47    }

    48
    49    ISerializable Members
    66}


    Default.aspx

    Default Page Html Code

    Default.aspx.cs

     1using System;
     2using System.Data;
     3using System.Configuration;
     4using System.Web;
     5using System.Web.Security;
     6using System.Web.UI;
     7using System.Web.UI.WebControls;
     8using System.Web.UI.WebControls.WebParts;
     9using System.Web.UI.HtmlControls;
    10using Ilungasoft.Framework.Common;
    11
    12public partial class _Default : Ilungasoft.Framework.Web.UI.Page 
    13{
    14    private User user = null;
    15    private Group group = null;
    16    private UserAndGroup userAndGroup;
    17    private UserAndGroup2 userAndGroup2;
    18
    19    protected void Page_Load(object sender, EventArgs e)
    20    {
    21        if (!IsPostBack)
    22        {
    23            SerialNumberValidator1.Create();
    24        }

    25
    26        user = EntityFactory<User>.CreateObject();
    27        user.ID = 1;
    28        user.Name = "teddy";
    29        user.Salary = 1000;
    30
    31        group = EntityFactory<Group>.CreateObject();
    32        group.ID = 2;
    33        group.Title = ".net group";
    34
    35        userAndGroup = new UserAndGroup();
    36        userAndGroup.SomeUser = user;
    37        userAndGroup.SomeGroups = new Group[] { group, group, group };
    38
    39        userAndGroup2 = new UserAndGroup2();
    40        userAndGroup2.SomeUser = user;
    41        userAndGroup2.SomeGroups = new Group[] { group, group, group };
    42    }

    43
    44    protected void btnBasicSerializeEntity_Click(object sender, EventArgs e)
    45    {
    46        TextBox1.Text = SerializeHelper.Serialize(user);
    47        User _user = SerializeHelper.Deserialize<User>(EntityFactory<User>.GetDynamicEntityType(), TextBox1.Text);
    48    }

    49    protected void btnBasicSerializeEntityArray_Click(object sender, EventArgs e)
    50    {
    51        TextBox1.Text = SerializeHelper.SerializeArray(new User[] { user, user, user });
    52        User[] _users = SerializeHelper.Deserialize<User[]>(EntityFactory<User>.GetDynamicEntityType().MakeArrayType(), TextBox1.Text);
    53    }

    54    protected void btnSoapSerializeEntity_Click(object sender, EventArgs e)
    55    {
    56        TextBox1.Text = SerializeHelper.SoapSerialize(user);
    57        //error when deserialize
    58        //User _user = SerializeHelper.SoapDeserialize<User>(TextBox1.Text);
    59    }

    60    protected void btnSoapSerializeEntityArray_Click(object sender, EventArgs e)
    61    {
    62        TextBox1.Text = SerializeHelper.SoapSerialize(new User[] { user, user, user });
    63        //error when deserialize
    64        //User[] _users = SerializeHelper.SoapDeserialize<User[]>(TextBox1.Text);
    65    }

    66    protected void btnComplexSoapDefault_Click(object sender, EventArgs e)
    67    {
    68        TextBox1.Text = SerializeHelper.SoapSerialize(userAndGroup);
    69        //error when deserialize
    70        //UserAndGroup _userAndGroup = SerializeHelper.SoapDeserialize<UserAndGroup>(TextBox1.Text);
    71    }

    72    protected void btnComplexSoapCustom_Click(object sender, EventArgs e)
    73    {
    74        TextBox1.Text = SerializeHelper.SoapSerialize(userAndGroup2);
    75        UserAndGroup2 _userAndGroup2 = SerializeHelper.SoapDeserialize<UserAndGroup2>(TextBox1.Text);
    76    }

    77    protected void btnCheckSerialNumber_Click(object sender, EventArgs e)
    78    {
    79        if (SerialNumberValidator1.CheckSN(TextBox2.Text))
    80        {
    81            Response.Write(ClientScriptFactory.WrapScriptTag(ClientScriptFactory.PopAlert("Serial Number Correct!")));
    82        }

    83        else
    84        {
    85            Response.Write(ClientScriptFactory.WrapScriptTag(ClientScriptFactory.PopAlert("Serial Number Error!")));
    86        }

    87    }

    88}

    好了,代码就是这些,我来分析一下。首先,如果您已经下载,并在vs IDE中打开示例源码,请注意Default.aspx中的红色的按钮,红色表示按钮代表的使用方式会出错。

    更正:经过代码更正,如果您下载的是v1.2.1 build 0408,那么Sample3中所有的序列化和反序列化方式都能正确运行。

    让我们一个一个按钮来看:

    1、Basic Single Entity和Basic Entity Array是两个最常用的功能,即使用XmlSerializer对单个的Entity实例或Entity[]数组进行序列化。这两个功能是完全正常的,推荐大家使用,自行运行示例程序查看生成的Xml。

    2、Soap Single Entity和Soap Entity Array就开始有问题了。如果您运行示例,他会正常显示序列化后的结果。但是非常遗憾,反序列化将不能正常运行,因为我们的Entity,如这里的User,用户实际上只定义了interface,具体的User类,有框架在运行时通过Emit的方式生成,而SoapFormatter内部默认实现是在后台构造编译一个dll来处理反序列化的,他会试图在文件系统查找定义了Entity的程序集,但是很遗憾,这个程序集只存在内存中,不会在文件系统中,因此就会运行出错。

    更正:经过代码更正,默认的Soap序列化完全能够正确执行了,因为将emit的动态程序集加入了AppDomain,反序列化时就不会有找不到动态程序集的问题了。

    3、我们再来看看下一个倒霉鬼Complex Default Soap。

    更正:Complex Default Soap也不再倒霉了,完全都能运行正常了!

    首先,我们看看程序要序列化的对象UserAndGroup,Entities.cs Line32-37。它是一个包含了一个User和一个Group[]数组的复合类。如果使用,XmlSerializer来序列化,很遗憾,会失败,因为,XmlSerializer傻乎乎的只会判断声明的类型,他发现声明的类型如User是一个interface,所以就不允许序列化。如果具体的Entity类型可以直接访问,我们知道,可以通过为Field设置XmlElemenAtttribute属性类指定具体的Entity类型,但是很遗憾,这个类型我们只能通过Entityfactory<IEntityType>.GetDynamicEntityType()获得,但XmlElemenAtttribute只接受常量类型作为参数。

    那么,SoapFormatter可不可以序列化呢?可以的!但是,就如之前的两个倒霉鬼一样,反序列化时会出错——找不到动态程序集。

    4、现在剩下最后一个Complex Custom Soap了,同样是Soap,只要我们的组合类型UserAndGroup2实现ISerializable接口,那么SoapFormatter将不会调用其默认的序列化逻辑,转而调用自定义的序列化逻辑。请看Entities.cs Line 49-65. 注意,对于序列化和反序列化,分别由一个函数GetObjectData和一个包含同样参数列表的构造函数对应。也就是说,当序列化发生时,GetObjectData会被调用执行自定义的序列化,而反序列化时,构造函数被执行。SerializeHelper.LoadField等辅助函数用以简化自定义序列化过程,非常简单就不多解释了。

    很幸运,通过这种方式,我们序列化和反序列化复合类UserAndGroup2的目的成功的达到了。

    小结

    讨论了这么久,我们有什么收获呢?

    1、首先,确认了一点,Ilungasoft Framework下的Entity、Entity Array和复合类型,都可以进行序列化和反序列化,但是并不是所有.Net Framework提供的序列化方式都能支持。

    2、要序列化对象,推荐调用SerializeHelper中定义的helper函数,参照范例。

    3、如果类型基于Emit生成,那么注意,必须将emit动态生成的程序集加载到AppDomain,否则,.Net Framework中预定义的那些通过在后台动态编译的函数可能会不能正确运行。

    4、功能实现中的任何便利,可能都会有代价,应注意取舍。

    5、还有一点前面没提到的,SoapFormatter不支持泛型类型的的序列化,不知为什么微软不支持,还是当前版本没来得及加上。

    补充:那么,文中更增了那么多,到底怎样将emit生成的动态程序集加载到AppDomain呢?

    只需要这样:


     1        private static ResolveEventHandler _ResolveEventHandler = null;
     2
     3        private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
     4        {
     5            return assBuilder;
     6        }

     7
     8//
     9
    10            //create dynamic IEntityType Assembly & Type through Emit
    11            if (assBuilder == null)
    12            {
    13                AssemblyName assName = new AssemblyName();
    14                assName.Name = DYNAMIC_ENTITY_NAMESPACE;
    15                assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
    16
    17                if (_ResolveEventHandler == null)
    18                {
    19                    _ResolveEventHandler = new ResolveEventHandler(CurrentDomain_AssemblyResolve);
    20                    AppDomain.CurrentDomain.AssemblyResolve += _ResolveEventHandler;
    21                }

    22            }

    23
    24//


    好了,讨论就到这儿。欢迎各位,尤其是对序列化经验丰富的朋友多提提意见和建议,看看怎样进一步改进、简化本框架下的序列化支持。

    Enjoy!

  • 相关阅读:
    (三)Java秒杀项目之实现秒杀功能
    多模块环境下修改包名Rename directory与Rename package
    pom.xml标签页名称
    mac终端命令
    @Select 数据表的字段与实体类的属性值
    Markedown换行
    链表问题-不开辟新空间
    Java机器学习框架(1)【待完成】
    奇妙的算法【3】- 贪心算法【待完成】
    奇妙的算法【2】- 韩信点兵问题优化
  • 原文地址:https://www.cnblogs.com/teddyma/p/369404.html
Copyright © 2011-2022 走看看