更正:本文最初的论述有一些错误,在双鱼座的指点下,只需将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

以上代码,比较简单,主要就是封装了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
66
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

66

Default.aspx

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
}

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

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
//

2

3

4

5

6

7

8


9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24


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