下载示例源码
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方便生成,这里也就不列举了,和数据库表、视图名称、字段是一一对应的):
1using System;
2using System.Data;
3using System.Data.Common;
4using System.Configuration;
5using System.Web;
6using System.Web.Security;
7using System.Web.UI;
8using System.Web.UI.WebControls;
9using System.Web.UI.WebControls.WebParts;
10using System.Web.UI.HtmlControls;
11
12public 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}
2using System.Data;
3using System.Data.Common;
4using System.Configuration;
5using System.Web;
6using System.Web.Security;
7using System.Web.UI;
8using System.Web.UI.WebControls;
9using System.Web.UI.WebControls.WebParts;
10using System.Web.UI.HtmlControls;
11
12public 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}
这里都是非常简单的查询和绑定代码,具体的效果大家可以直接运行示例来看。在这里您只需关注前述视图的使用,可以看到,如此进行正反查询时非常便利的,而当插入新数据时,当然是直接操作UserGroup表,也很容易。
总结
让我们来总结一下该方案相对传统ORM方案的主要优缺点。
优点:本方案无需配置,引用程序集就能使用,上手容易,起点低,使用灵活,能够快速应对需求变化,强类型不易出错,还有一点很重要,需要用户写的代码极大减少;
缺点:需要建较多视图,数据底层对使用者不够透明。
欢迎大家评判、指教!