本文主要探讨关于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
1
using System;
2
using System.Collections;
3
using System.IO;
4
using System.Text;
5
using System.Collections.Generic;
6
using System.Xml;
7
using System.Xml.Serialization;
8
using System.Runtime.Serialization;
9
using System.Runtime.Serialization.Formatters.Soap;
10
using System.Runtime.Serialization.Formatters.Binary;
11
12
namespace Ilungasoft.Framework.Common
13

{
14
public class SerializeHelper
15
{
16
private static string RemoveXmlHeader(string xml)
17
{
18
if (xml.StartsWith("<?"))
19
{
20
return xml.Substring(xml.IndexOf("?>") + 2);
21
}
22
else
23
{
24
return xml;
25
}
26
}
27
28
public static string Serialize(object obj)
29
{
30
if (obj == null)
31
{
32
return string.Empty;
33
}
34
35
XmlSerializer s = new XmlSerializer(obj.GetType());
36
StringBuilder sb = new StringBuilder();
37
s.Serialize(new StringWriter(sb), obj);
38
return RemoveXmlHeader(sb.ToString());
39
}
40
41
public static void Serialize(Stream stream, object obj)
42
{
43
if (obj == null)
44
{
45
return;
46
}
47
48
XmlSerializer s = new XmlSerializer(obj.GetType());
49
s.Serialize(stream, obj);
50
}
51
52
public static string SerializeArray(object[] objs)
53
{
54
if (objs != null && objs.Length > 0)
55
{
56
XmlSerializer s = new XmlSerializer(objs[0].GetType().MakeArrayType());
57
StringBuilder sb = new StringBuilder();
58
s.Serialize(new StringWriter(sb), objs);
59
return RemoveXmlHeader(sb.ToString());
60
}
61
else
62
{
63
return string.Empty;
64
}
65
}
66
67
public static void SerializeArray(Stream stream, object[] objs)
68
{
69
if (objs != null && objs.Length > 0)
70
{
71
XmlSerializer s = new XmlSerializer(objs[0].GetType().MakeArrayType());
72
s.Serialize(stream, objs);
73
}
74
else
75
{
76
return;
77
}
78
}
79
80
public static ReturnType Deserialize<ReturnType>(Type originType, string xml)
81
{
82
XmlSerializer s = new XmlSerializer(originType);
83
return (ReturnType)s.Deserialize(new StringReader(xml));
84
}
85
86
public static string SoapSerialize(object obj)
87
{
88
SoapFormatter formatter = new SoapFormatter();
89
MemoryStream stream = new MemoryStream();
90
formatter.Serialize(stream, obj);
91
return System.Text.UTF8Encoding.UTF8.GetString(stream.ToArray());
92
}
93
94
public static void SoapSerialize(Stream stream, object obj)
95
{
96
SoapFormatter formatter = new SoapFormatter();
97
formatter.Serialize(stream, obj);
98
}
99
100
public static ReturnType SoapDeserialize<ReturnType>(string xml)
101
{
102
SoapFormatter formatter = new SoapFormatter();
103
MemoryStream stream = new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(xml));
104
return (ReturnType)formatter.Deserialize(stream);
105
}
106
107
public static ReturnType SoapDeserialize<ReturnType>(Stream stream)
108
{
109
SoapFormatter formatter = new SoapFormatter();
110
return (ReturnType)formatter.Deserialize(stream);
111
}
112
113
public static FieldType LoadField<FieldType>(string elementName, SerializationInfo info)
114
where FieldType : IEntity
115
{
116
string xml = info.GetString(elementName);
117
return xml == null ? default(FieldType) : (FieldType)SerializeHelper.Deserialize<FieldType>(EntityFactory<FieldType>.GetDynamicEntityType(), xml);
118
}
119
120
public static ItemType[] LoadArrayField<ItemType>(string elementName, SerializationInfo info)
121
where ItemType : IEntity
122
{
123
string xml = info.GetString(elementName);
124
return xml == null ? default(ItemType[]) : SerializeHelper.Deserialize<ItemType[]>(EntityFactory<ItemType>.GetDynamicEntityType().MakeArrayType(), xml);
125
}
126
127
public static void SaveField(string elementName, object value, SerializationInfo info)
128
{
129
if (value != null)
130
{
131
info.AddValue(elementName, SerializeHelper.Serialize(value));
132
}
133
}
134
135
public static void SaveArrayField(string elementName, object[] values, SerializationInfo info)
136
{
137
if (values != null)
138
{
139
info.AddValue(elementName, SerializeHelper.SerializeArray(values));
140
}
141
}
142
}
143
}
以上代码,比较简单,主要就是封装了XmlSerializer和SoapFormatter这两个类,最常用的操作,下面会结合示例进行说明。
下面就是我们的示例的代码:
Entities.cs
Default.aspx
Default Page Html Code
1
<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" ValidateRequest="false" %>
2
3
<%
@ Register Assembly="Framework.Web" Namespace="WebValidates" TagPrefix="cc1" %>
4
5
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
7
<html xmlns="http://www.w3.org/1999/xhtml" >
8
<head runat="server">
9
<title>Untitled Page</title>
10
</head>
11
<body>
12
<form id="form1" runat="server">
13
<div>
14
<asp:Button ID="btnBasicSerializeEntity" runat="server" OnClick="btnBasicSerializeEntity_Click"
15
Text="Basic Single Entity" />
16
<asp:Button ID="btnBasicSerializeEntityArray" runat="server" OnClick="btnBasicSerializeEntityArray_Click"
17
Text="Basic Entity Array" />
18
<asp:Button ID="btnSoapSerializeEntity" runat="server" ForeColor="Red" OnClick="btnSoapSerializeEntity_Click"
19
Text="Soap Signle Entity" />
20
<asp:Button ID="btnSoapSerializeEntityArray" runat="server" ForeColor="Red" OnClick="btnSoapSerializeEntityArray_Click"
21
Text="Soap Enityt Array" /><br />
22
<br />
23
<asp:Button ID="btnComplexSoapDefault" runat="server" ForeColor="Red" OnClick="btnComplexSoapDefault_Click"
24
Text="Complex Default Soap" />
25
<asp:Button ID="btnComplexSoapCustom" runat="server" OnClick="btnComplexSoapCustom_Click"
26
Text="Complex Custom Soap" /><br />
27
<br />
28
<asp:TextBox ID="TextBox1" runat="server" Height="400px" TextMode="MultiLine" Width="700px"></asp:TextBox><br />
29
<br />
30
<cc1:serialnumbervalidator id="SerialNumberValidator1" runat="server"></cc1:serialnumbervalidator>
31
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>
32
<asp:Button ID="btnCheckSerialNumber" runat="server" OnClick="btnCheckSerialNumber_Click"
33
Text="Check Serial Number" /></div>
34
</form>
35
</body>
36
</html>
Default.aspx.cs
好了,代码就是这些,我来分析一下。首先,如果您已经下载,并在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呢?
只需要这样:
好了,讨论就到这儿。欢迎各位,尤其是对序列化经验丰富的朋友多提提意见和建议,看看怎样进一步改进、简化本框架下的序列化支持。
Enjoy!
更正:本文最初的论述有一些错误,在双鱼座的指点下,只需将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
1
using System;2
using System.Collections;3
using System.IO;4
using System.Text;5
using System.Collections.Generic;6
using System.Xml;7
using System.Xml.Serialization;8
using System.Runtime.Serialization;9
using System.Runtime.Serialization.Formatters.Soap;10
using System.Runtime.Serialization.Formatters.Binary;11

12
namespace Ilungasoft.Framework.Common13


{14
public class SerializeHelper15

{16
private static string RemoveXmlHeader(string xml)17

{18
if (xml.StartsWith("<?"))19

{20
return xml.Substring(xml.IndexOf("?>") + 2);21
}22
else23

{24
return xml;25
}26
}27

28
public static string Serialize(object obj)29

{30
if (obj == null)31

{32
return string.Empty;33
}34

35
XmlSerializer s = new XmlSerializer(obj.GetType());36
StringBuilder sb = new StringBuilder();37
s.Serialize(new StringWriter(sb), obj);38
return RemoveXmlHeader(sb.ToString());39
}40

41
public static void Serialize(Stream stream, object obj)42

{43
if (obj == null)44

{45
return;46
}47

48
XmlSerializer s = new XmlSerializer(obj.GetType());49
s.Serialize(stream, obj);50
}51

52
public static string SerializeArray(object[] objs)53

{54
if (objs != null && objs.Length > 0)55

{56
XmlSerializer s = new XmlSerializer(objs[0].GetType().MakeArrayType());57
StringBuilder sb = new StringBuilder();58
s.Serialize(new StringWriter(sb), objs);59
return RemoveXmlHeader(sb.ToString());60
}61
else62

{63
return string.Empty;64
}65
}66

67
public static void SerializeArray(Stream stream, object[] objs)68

{69
if (objs != null && objs.Length > 0)70

{71
XmlSerializer s = new XmlSerializer(objs[0].GetType().MakeArrayType());72
s.Serialize(stream, objs);73
}74
else75

{76
return;77
}78
}79

80
public static ReturnType Deserialize<ReturnType>(Type originType, string xml)81

{82
XmlSerializer s = new XmlSerializer(originType);83
return (ReturnType)s.Deserialize(new StringReader(xml));84
}85

86
public static string SoapSerialize(object obj)87

{88
SoapFormatter formatter = new SoapFormatter();89
MemoryStream stream = new MemoryStream();90
formatter.Serialize(stream, obj);91
return System.Text.UTF8Encoding.UTF8.GetString(stream.ToArray());92
}93

94
public static void SoapSerialize(Stream stream, object obj)95

{96
SoapFormatter formatter = new SoapFormatter();97
formatter.Serialize(stream, obj);98
}99

100
public static ReturnType SoapDeserialize<ReturnType>(string xml)101

{102
SoapFormatter formatter = new SoapFormatter();103
MemoryStream stream = new MemoryStream(System.Text.UTF8Encoding.UTF8.GetBytes(xml));104
return (ReturnType)formatter.Deserialize(stream);105
}106

107
public static ReturnType SoapDeserialize<ReturnType>(Stream stream)108

{109
SoapFormatter formatter = new SoapFormatter();110
return (ReturnType)formatter.Deserialize(stream);111
}112

113
public static FieldType LoadField<FieldType>(string elementName, SerializationInfo info)114
where FieldType : IEntity115

{116
string xml = info.GetString(elementName);117
return xml == null ? default(FieldType) : (FieldType)SerializeHelper.Deserialize<FieldType>(EntityFactory<FieldType>.GetDynamicEntityType(), xml);118
}119

120
public static ItemType[] LoadArrayField<ItemType>(string elementName, SerializationInfo info)121
where ItemType : IEntity122

{123
string xml = info.GetString(elementName);124
return xml == null ? default(ItemType[]) : SerializeHelper.Deserialize<ItemType[]>(EntityFactory<ItemType>.GetDynamicEntityType().MakeArrayType(), xml);125
}126

127
public static void SaveField(string elementName, object value, SerializationInfo info)128

{129
if (value != null)130

{131
info.AddValue(elementName, SerializeHelper.Serialize(value));132
}133
}134

135
public static void SaveArrayField(string elementName, object[] values, SerializationInfo info)136

{137
if (values != null)138

{139
info.AddValue(elementName, SerializeHelper.SerializeArray(values));140
}141
}142
}143
}
以上代码,比较简单,主要就是封装了XmlSerializer和SoapFormatter这两个类,最常用的操作,下面会结合示例进行说明。
下面就是我们的示例的代码:
Entities.cs
1
using
System;
2
using
System.Data;
3
using
System.Configuration;
4
using
System.Web;
5
using
System.Web.Security;
6
using
System.Web.UI;
7
using
System.Web.UI.WebControls;
8
using
System.Web.UI.WebControls.WebParts;
9
using
System.Web.UI.HtmlControls;
10
using
System.Xml.Serialization;
11
using
System.Runtime.Serialization;
12
using
Ilungasoft.Framework.Common;
13
using
System.Security.Permissions;
14
15
public
interface
Group : IEntity
16
{
17
int ID
{ get; set; }
18
string Title
{ get; set; }
19
string Description
{ get; set; }
20
System.DateTime CreateTime
{ get; set; }
21
int ParentID
{ get; set; }
22
}
23
24
public
interface
User : IEntity
25
{
26
int ID
{ get; set; }
27
string Name
{ get; set; }
28
bool Gender
{ get; set; }
29
double Salary
{ get; set; }
30
}
31
32
[Serializable]
33
public
class
UserAndGroup
34
{
35
public User SomeUser;
36
public Group[] SomeGroups;
37
}
38
39
[Serializable]
40
public
class
UserAndGroup2 : ISerializable
41
{
42
public User SomeUser;
43
public Group[] SomeGroups;
44
45
public UserAndGroup2()
46
{
47
}
48
49
ISerializable Members#region ISerializable Members
50
51
protected UserAndGroup2(SerializationInfo info, StreamingContext context)
52
{
53
SomeUser = SerializeHelper.LoadField<User>("SomeUser", info);
54
SomeGroups = SerializeHelper.LoadArrayField<Group>("SomeGroups", info);
55
}
56
57
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
58
Flags = SecurityPermissionFlag.SerializationFormatter)]
59
public void GetObjectData(SerializationInfo info, StreamingContext context)
60
{
61
SerializeHelper.SaveField("SomeUser", SomeUser, info);
62
SerializeHelper.SaveArrayField("SomeGroups", SomeGroups, info);
63
}
64
65
#endregion
66
}
using
System;2
using
System.Data;3
using
System.Configuration;4
using
System.Web;5
using
System.Web.Security;6
using
System.Web.UI;7
using
System.Web.UI.WebControls;8
using
System.Web.UI.WebControls.WebParts;9
using
System.Web.UI.HtmlControls;10
using
System.Xml.Serialization;11
using
System.Runtime.Serialization;12
using
Ilungasoft.Framework.Common;13
using
System.Security.Permissions;14

15
public
interface
Group : IEntity16

{17

int ID
{ get; set; }18

string Title
{ get; set; }19

string Description
{ get; set; }20

System.DateTime CreateTime
{ get; set; }21

int ParentID
{ get; set; }22
}
23

24
public
interface
User : IEntity25

{26

int ID
{ get; set; }27

string Name
{ get; set; }28

bool Gender
{ get; set; }29

double Salary
{ get; set; }30
}
31

32
[Serializable]33
public
class
UserAndGroup34

{35
public User SomeUser;36
public Group[] SomeGroups;37
}
38

39
[Serializable]40
public
class
UserAndGroup2 : ISerializable41

{42
public User SomeUser;43
public Group[] SomeGroups;44

45
public UserAndGroup2()46

{47
}48

49

ISerializable Members#region ISerializable Members50

51
protected UserAndGroup2(SerializationInfo info, StreamingContext context)52

{53
SomeUser = SerializeHelper.LoadField<User>("SomeUser", info);54
SomeGroups = SerializeHelper.LoadArrayField<Group>("SomeGroups", info);55
}56

57
[SecurityPermissionAttribute(SecurityAction.LinkDemand,58
Flags = SecurityPermissionFlag.SerializationFormatter)]59
public void GetObjectData(SerializationInfo info, StreamingContext context)60

{61
SerializeHelper.SaveField("SomeUser", SomeUser, info);62
SerializeHelper.SaveArrayField("SomeGroups", SomeGroups, info);63
}64

65
#endregion66
}
Default.aspx
1

<%
@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" ValidateRequest="false" %>2

3

<%
@ Register Assembly="Framework.Web" Namespace="WebValidates" TagPrefix="cc1" %>4

5
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">6

7
<html xmlns="http://www.w3.org/1999/xhtml" >8
<head runat="server">9
<title>Untitled Page</title>10
</head>11
<body>12
<form id="form1" runat="server">13
<div>14
<asp:Button ID="btnBasicSerializeEntity" runat="server" OnClick="btnBasicSerializeEntity_Click"15
Text="Basic Single Entity" />16
<asp:Button ID="btnBasicSerializeEntityArray" runat="server" OnClick="btnBasicSerializeEntityArray_Click"17
Text="Basic Entity Array" />18
<asp:Button ID="btnSoapSerializeEntity" runat="server" ForeColor="Red" OnClick="btnSoapSerializeEntity_Click"19
Text="Soap Signle Entity" />20
<asp:Button ID="btnSoapSerializeEntityArray" runat="server" ForeColor="Red" OnClick="btnSoapSerializeEntityArray_Click"21
Text="Soap Enityt Array" /><br />22
<br />23
<asp:Button ID="btnComplexSoapDefault" runat="server" ForeColor="Red" OnClick="btnComplexSoapDefault_Click"24
Text="Complex Default Soap" />25
<asp:Button ID="btnComplexSoapCustom" runat="server" OnClick="btnComplexSoapCustom_Click"26
Text="Complex Custom Soap" /><br />27
<br />28
<asp:TextBox ID="TextBox1" runat="server" Height="400px" TextMode="MultiLine" Width="700px"></asp:TextBox><br />29
<br />30
<cc1:serialnumbervalidator id="SerialNumberValidator1" runat="server"></cc1:serialnumbervalidator>31
<asp:TextBox ID="TextBox2" runat="server"></asp:TextBox>32
<asp:Button ID="btnCheckSerialNumber" runat="server" OnClick="btnCheckSerialNumber_Click"33
Text="Check Serial Number" /></div>34
</form>35
</body>36
</html>
Default.aspx.cs
1
using
System;
2
using
System.Data;
3
using
System.Configuration;
4
using
System.Web;
5
using
System.Web.Security;
6
using
System.Web.UI;
7
using
System.Web.UI.WebControls;
8
using
System.Web.UI.WebControls.WebParts;
9
using
System.Web.UI.HtmlControls;
10
using
Ilungasoft.Framework.Common;
11
12
public
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
}
using
System;2
using
System.Data;3
using
System.Configuration;4
using
System.Web;5
using
System.Web.Security;6
using
System.Web.UI;7
using
System.Web.UI.WebControls;8
using
System.Web.UI.WebControls.WebParts;9
using
System.Web.UI.HtmlControls;10
using
Ilungasoft.Framework.Common;11

12
public
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 deserialize58
//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 deserialize64
//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 deserialize70
//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
else84

{85
Response.Write(ClientScriptFactory.WrapScriptTag(ClientScriptFactory.PopAlert("Serial Number Error!")));86
}87
}88
}
好了,代码就是这些,我来分析一下。
更正:经过代码更正,如果您下载的是v1.2.1 build 0408,那么Sample3中所有的序列化和反序列化方式都能正确运行。
让我们一个一个按钮来看:
1、Basic Single Entity和Basic Entity Array是两个最常用的功能,即使用XmlSerializer对单个的Entity实例或Entity[]数组进行序列化。这两个功能是完全正常的,推荐大家使用,自行运行示例程序查看生成的Xml。
2、
更正:经过代码更正,默认的Soap序列化完全能够正确执行了,因为将emit的动态程序集加入了AppDomain,反序列化时就不会有找不到动态程序集的问题了。
3、
更正: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
//
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!

探讨IlungasoftFramework中动态Entity的序列化问题,包括使用XmlSerializer和SoapFormatter进行序列化的方法,以及如何解决序列化时遇到的限制。

被折叠的 条评论
为什么被折叠?



