下载示例源码
Sample2.zip
分析
实际上,Ilungasoft Framework不被设计成一个完全的ORM,而是一个强类型的泛型数据访问接口。因此,基于Ilungasoft Framework开发时,推荐使用视图来描述复杂查询,也推荐采用视图来简化有关联关系的实体访问。
在本示例中,共涉及四张表:User、Group、UserGroup、Message。大家从名字就能猜测其关系。User表示人员,Group表示组,人员和组是多对多关系,UserGroup就是关联表;而Message的FromUserID和ToUserID则分别和User的ID关联,也就是说,他们的关系是一对多;同时,Group是一个两层的组织结构,也就是说,Group包含下级的Group,通过Group中的字段ParentID来区分——ParentID为0表示顶层Group,否则ParentID为父Group的ID。
那么,在Ilungasoft Framework方式下,怎样简化处理这样的关联关系呢?没错,就是前面就提到的——视图。只需要建三个视图就能基本满足需求,方便起见,我这里直接列出视图的SQL,要直观的查看关系的话,请打开下载示例中App_Code目录下的mdb文件。
vUser
SELECT User.ID, User.Name, User.Gender, User.Salary, UserGroup.GroupID
FROM UserGroup INNER JOIN [User] ON UserGroup.UserID = User.ID;
vGroup
SELECT Group.ID, Group.Title, Group.Description, Group.CreateTime, Group.ParentID, UserGroup.UserID
FROM [Group] INNER JOIN UserGroup ON Group.ID = UserGroup.GroupID;
vMessage
SELECT Message.ID, Message.FromUserID, User.Name AS FromUserName, Message.ToUserID, User_1.Name AS ToUserName, Message.Title, Message.Content, Message.SendTime
FROM (Message INNER JOIN [User] ON Message.FromUserID = User.ID) INNER JOIN [User] AS User_1 ON Message.ToUserID = User_1.ID;
我们可以看到:vUser可以用于通过GroupID对User进行查询,vGroup可用于通过UserID查询Group,这就通过逻辑视图将一个多对多关联的三个表,转换为了两对一对多的逻辑视图。而对于,一对多的关系,通过外键字段查询关联关连实体本身就比较自然,不需为关联关系作特别处理,只是我希望查询Message时,能一并获得发送人和接受人的名字,因此,建了vMessage这个视图,这又体现了该方案相对于传统ORM方案的灵活性。
代码
那么来看看在代码中如何查询、更新这些表和视图吧(这里就只列出后代查询代码了,具体效果请自行运行示例,实体类代码可以由EntityGen方便生成,这里也就不列举了,和数据库表、视图名称、字段是一一对应的):
1
using System;
2
using System.Data;
3
using System.Data.Common;
4
using System.Configuration;
5
using System.Web;
6
using System.Web.Security;
7
using System.Web.UI;
8
using System.Web.UI.WebControls;
9
using System.Web.UI.WebControls.WebParts;
10
using System.Web.UI.HtmlControls;
11
12
public partial class _Default : System.Web.UI.Page
13
{
14
protected void Page_Load(object sender, EventArgs e)
15
{
16
if (!IsPostBack)
17
{
18
Entity.Group[] groups = Facade.Select<Entity.Group>("ParentID = 0");
19
listGroups.DataSource = groups;
20
listGroups.DataTextField = "Title";
21
listGroups.DataValueField = "ID";
22
23
Entity.User[] users = Facade.SelectAll<Entity.User>();
24
listUsers.DataSource = users;
25
listUsers.DataTextField = "Name";
26
listUsers.DataValueField = "ID";
27
28
DataBind();
29
30
RefreshSubGroups();
31
}
32
}
33
34
private void RefreshSubGroups()
35
{
36
Entity.Group[] subGroups = Facade.Select<Entity.Group>("ParentID = @ParentID", int.Parse(listGroups.SelectedValue));
37
Entity.Group[] list = new Entity.Group[subGroups.Length + 1];
38
Entity.Group allSubGroup = Facade.Create<Entity.Group>();
39
allSubGroup.Title = "All";
40
list[0] = allSubGroup;
41
subGroups.CopyTo(list, 1);
42
listSubGroups.DataSource = list;
43
listSubGroups.DataTextField = "Title";
44
listSubGroups.DataValueField = "ID";
45
46
listSubGroups.DataBind();
47
}
48
49
protected void btnViewUsersInGroup_Click(object sender, EventArgs e)
50
{
51
int selectedSubGroup = int.Parse(listSubGroups.SelectedValue);
52
Entity.vUser[] usersInGroup = Facade.Select<Entity.vUser>("GroupID = @GroupID", selectedSubGroup > 0 ? selectedSubGroup : int.Parse(listGroups.SelectedValue));
53
gridView.DataSource = usersInGroup;
54
DataBind();
55
}
56
57
protected void btnViewGroupsOfUser_Click(object sender, EventArgs e)
58
{
59
Entity.vGroup[] groupsOfUser = Facade.Select<Entity.vGroup>("UserID = @UserID", int.Parse(listUsers.SelectedValue));
60
gridView.DataSource = groupsOfUser;
61
DataBind();
62
}
63
64
protected void btnViewMessages_Click(object sender, EventArgs e)
65
{
66
Entity.vMessage[] messages = Facade.SelectAll<Entity.vMessage>();
67
gridView.DataSource = messages;
68
DataBind();
69
}
70
71
protected void btnTestAddUserToGroup_Click(object sender, EventArgs e)
72
{
73
Entity.UserGroup[] userGroups = Facade.Select<Entity.UserGroup>("GroupID", 1, 1, "UserID = @UserID and GroupID = @GroupID", int.Parse(listUsers.SelectedValue), int.Parse(listGroups.SelectedValue));
74
75
DbTransaction tran = Facade.BeginTransaction();
76
77
try
78
{
79
if (userGroups != null && userGroups.Length > 0)
80
{
81
Facade.Delete<Entity.UserGroup>("UserID = @UserID and GroupID = @GroupID", userGroups[0].UserID, userGroups[0].GroupID, tran);
82
}
83
84
Entity.UserGroup newUserGroup = Facade.Create<Entity.UserGroup>();
85
newUserGroup.UserID = int.Parse(listUsers.SelectedValue);
86
newUserGroup.GroupID = int.Parse(listGroups.SelectedValue);
87
Facade.Insert<Entity.UserGroup>(newUserGroup, tran);
88
89
newUserGroup.GroupID = int.Parse(listSubGroups.SelectedValue);
90
Facade.Insert<Entity.UserGroup>(newUserGroup, tran);
91
92
tran.Commit();
93
94
btnViewUsersInGroup_Click(null, null);
95
}
96
catch
97
{
98
tran.Rollback();
99
throw;
100
}
101
finally
102
{
103
Facade.CloseTransaction(tran);
104
}
105
}
106
107
protected void listGroups_SelectedIndexChanged(object sender, EventArgs e)
108
{
109
RefreshSubGroups();
110
}
111
}

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

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

这里都是非常简单的查询和绑定代码,具体的效果大家可以直接运行示例来看。在这里您只需关注前述视图的使用,可以看到,如此进行正反查询时非常便利的,而当插入新数据时,当然是直接操作UserGroup表,也很容易。
总结
让我们来总结一下该方案相对传统ORM方案的主要优缺点。
优点:本方案无需配置,引用程序集就能使用,上手容易,起点低,使用灵活,能够快速应对需求变化,强类型不易出错,还有一点很重要,需要用户写的代码极大减少;
缺点:需要建较多视图,数据底层对使用者不够透明。
欢迎大家评判、指教!