http://www.cnblogs.com/netfocus/archive/2010/01/10/1643207.html
上面这个是框架发布页面的地址。
就以论坛版块管理模块作为例子来介绍这个框架吧,包括显示版块列表、新增版块、修改版块、删除版块四个功能;
1. 表设计:
2 [EntityId] [int] IDENTITY(1,1) NOT NULL,
3 [Subject] [varchar](128) NOT NULL,
4 [Enabled] [int] NOT NULL,
5 [GroupId] [int] NOT NULL,
6 [TotalThreads] [int] NOT NULL
7 ) ON [PRIMARY]
2. ROM配置文件:
2 <field name="EntityId" type="int" typeEnum="Int" size="4" isIdentity="true" />
3 <field name="Subject" type="varchar(128)" typeEnum="VarChar" size="128" />
4 <field name="Enabled" type="int" typeEnum="Int" size="1" />
5 <field name="GroupId" type="int" typeEnum="Int" size="4" />
6 <field name="TotalThreads" type="int" typeEnum="Int" size="4" />
7 </table>
8 <entityMapping entityType="Forum.Business.Section, Forum.Business" tableName="tb_Sections">
9 <propertyNode propertyName="EntityId" fieldName="EntityId" />
10 <propertyNode propertyName="Subject" fieldName="Subject" />
11 <propertyNode propertyName="Enabled" fieldName="Enabled" />
12 <propertyNode propertyName="GroupId" fieldName="GroupId" />
13 <propertyNode propertyName="TotalThreads" fieldName="TotalThreads" />
14 </entityMapping>
说明:table结点表示tb_Sections表的结构;entityMapping结点表示Section对象和tb_Sections表的对应关系;
需要特别说明的是,对象可以嵌套并和表或视图的字段对应;例如下面这个例子:
2 <propertyNode propertyName="EntityId" fieldName="EntityId" />
3 <propertyNode propertyName="User">
4 <propertyNode propertyName="EntityId" fieldName="UserId" />
5 <propertyNode propertyName="MemberId" fieldName="MemberId" />
6 <propertyNode propertyName="NickName" fieldName="NickName" />
7 <propertyNode propertyName="Email" fieldName="Email" />
8 <propertyNode propertyName="AvatarFileName" fieldName="AvatarFileName" />
9 <propertyNode propertyName="AvatarContent" fieldName="AvatarContent" />
10 <propertyNode propertyName="UserStatus" fieldName="UserStatus" />
11 <propertyNode propertyName="TotalMarks" fieldName="TotalMarks" />
12 </propertyNode>
13 <propertyNode propertyName="Role">
14 <propertyNode propertyName="EntityId" fieldName="RoleId" />
15 <propertyNode propertyName="Name" fieldName="RoleName" />
16 <propertyNode propertyName="Description" fieldName="RoleDescription" />
17 <propertyNode propertyName="RoleType" fieldName="RoleType" />
18 </propertyNode>
19 </entityMapping>
UserAndRole对象中包含了一个User对象和一个Role对象,UserAndRole对象和一个视图vw_RoleUsers对应;其中User对象中的一些属性和视图中的相关字段对应,Role对象也是一样;
3. 功能实现:
3.1 显示版块列表、删除版块功能的实现:
2 {
3 protected Repeater list;
4 protected ValuedDropDownList groupDropDownList;
5 protected AjaxPager pager;
6 protected CurrentPage currentPage;
7
8 public string AdminUserRoleId
9 {
10 get
11 {
12 string s = ViewState["AdminUserRoleId"] as string;
13 if (s == null)
14 {
15 s = string.Empty;
16 }
17 return s;
18 }
19 set
20 {
21 ViewState["AdminUserRoleId"] = value;
22 }
23 }
24
25 protected override void OnFirstLoad()
26 {
27 if (!ValidatePermission(PermissionType.SectionAdmin))
28 {
29 throw new Exception("您没有管理版块或版块组的权限!");
30 }
31 }
32 public override void GetRequests(List<RequestBinder> requestBinders)
33 {
34 requestBinders.Add(BinderBuilder.BuildGetAllBinder(this, new TRequest<Group>()));
35
36 TRequest<Section> request = new TRequest<Section>();
37 request.PageSize = pager.PageSize;
38 requestBinders.Add(BinderBuilder.BuildGetListBinder(this, request));
39 }
40 public override void GetReplies(List<RequestBinder> requestBinders)
41 {
42 BindGroupDropDownList(requestBinders[0].Reply.EntityList);
43 BindRepeater(requestBinders[1].Reply);
44 BindPager(requestBinders[1].Reply.TotalRecords, 1);
45 }
46
47 #region Ajax Methods
48
49 [AjaxMethod]
50 public void DeleteSection(int sectionId)
51 {
52 TRequest<Post> postRequest = new TRequest<Post>();
53 postRequest.Data.SectionId.Value = sectionId;
54
55 ThreadRequest threadRequest = new ThreadRequest();
56 threadRequest.Data.SectionId.Value = sectionId;
57
58 TRequest<SectionRoleUser> sectionRoleUserRequest = new TRequest<SectionRoleUser>();
59 sectionRoleUserRequest.Data.SectionId.Value = sectionId;
60
61 ProcessResult result = Engine.Executes(BinderBuilder.BuildDeleteListBinder(postRequest), BinderBuilder.BuildDeleteListBinder(threadRequest), BinderBuilder.BuildDeleteListBinder(sectionRoleUserRequest), BinderBuilder.BuildDeleteBinder<Section>(sectionId));
62 if (!result.IsSuccess)
63 {
64 throw new Exception(result.ErrorMessage);
65 }
66 }
67 [AjaxMethod]
68 public void DeleteSections(string items)
69 {
70 if (string.IsNullOrEmpty(items))
71 {
72 return;
73 }
74 int entityId = 0;
75 foreach (string item in items.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries))
76 {
77 entityId = Globals.ChangeType<int>(item);
78 if (entityId > 0)
79 {
80 DeleteSection(entityId);
81 }
82 }
83 }
84 [AjaxMethod]
85 public ListManageAjaxData RefreshList(int groupId, int pageIndex)
86 {
87 TRequest<Section> request = new TRequest<Section>();
88
89 request.Data.GroupId.Value = groupId;
90 request.PageIndex = pageIndex;
91 request.PageSize = pager.PageSize;
92
93 Reply reply = Engine.GetEntityList(request);
94
95 BindRepeater(reply);
96 BindPager(reply.TotalRecords, pageIndex);
97
98 ListManageAjaxData result = new ListManageAjaxData();
99 result.ListContent = Globals.RenderControl(list);
100 result.PagingContent = Globals.RenderControl(currentPage) + Globals.RenderControl(pager);
101
102 return result;
103 }
104
105 #endregion
106
107 #region Private Methods
108
109 private void BindGroupDropDownList(EntityList groups)
110 {
111 Group topGroup = new Group();
112 topGroup.Subject.Value = "所有版块组";
113 groups.Insert(0, topGroup);
114
115 groupDropDownList.DataSource = groups;
116 groupDropDownList.DataTextField = "Subject";
117 groupDropDownList.DataValueField = "EntityId";
118 groupDropDownList.DataBind();
119 }
120 private void BindRepeater(Reply reply)
121 {
122 AdminUserRoleId = RoleManager.GetRole(ForumConfiguration.Instance.ForumSectionAdminRoleName).EntityId.Value.ToString();
123 list.DataSource = reply.EntityList;
124 list.DataBind();
125 }
126 private void BindPager(int totalRecords, int pageIndex)
127 {
128 if (pager != null)
129 {
130 pager.TotalRecords = totalRecords;
131 pager.PageIndex = pageIndex;
132 if (currentPage != null)
133 {
134 currentPage.TotalRecords = pager.TotalRecords;
135 currentPage.TotalPages = pager.TotalPages;
136 currentPage.PageIndex = pager.PageIndex;
137 }
138 }
139 }
140
141 #endregion
142 }
说明:
OnFirstLoad函数在前页面第一次显示时被调用,我在该函数中进行了权限的判断:
2 {
3 if (!ValidatePermission(PermissionType.SectionAdmin))
4 {
5 throw new Exception("您没有管理版块或版块组的权限!");
6 }
7 }
在基类控件中规定如下的时候才会调用该方法:
2 {
3 if (!Page.IsPostBack && !Page.IsCallback && !AjaxManager.IsCallBack)
4 {
5 OnFirstLoad();
6 }
7 }
重写GetRequests函数使得各个具体控件有机会告诉框架当前的Url请求需要发送什么数据请求。GetRequests函数由框架负责调用,在这个例子中,创建了两个请求并添加到集合中:
2
3 TRequest<Section> request = new TRequest<Section>();
4 request.PageSize = pager.PageSize;
5 requestBinders.Add(BinderBuilder.BuildGetListBinder(this, request));
其中第一个请求目的是获取所有的版块分组,第二个请求目的是为了获取当前页下的所有版块信息;BuildGetAllBinder方法告诉框架我不需要分页获取数据,而是获取这类数据的全部记录;
BuildGetListBinder告诉框架应该获取当前PageIndex下的PageSize条记录;
另外,我们还重写了GetReplies方法,该方法由框架调用。当框架获取了所请求的数据后,会调用该方法,将请求的回复返回给请求者。在该方法中,我们可以对返回的数据进行处理,如绑定到Repeater控件和设置分页控件信息等。
2 {
3 BindGroupDropDownList(requestBinders[0].Reply.EntityList);
4 BindRepeater(requestBinders[1].Reply);
5 BindPager(requestBinders[1].Reply.TotalRecords, 1);
6 }
其中requestBinders[0]表示第一个请求的回复,requestBinders[1]表示第二个请求的回复;BindGroupDropDownList、BinderRepeater、BindPager三个函数分别用来将返回的数据绑定到控件;
通过GetRequests、以及GetReplies这两个函数,我们可以不必自己去关心该在什么时候去获取数据,而只要告诉框架我要获取什么数据即可;这样做的好处是,比如当前页面有10个用户控件,每个用户控件都需要获取自己所需要的数据,我们以往的做法是,每个控件自己去建立数据库连接并自己负责去获取数据并处理数据。这样做带来的后果是一次页面显示时,可能需要建立10次数据库连接,这样会影响性能;而如果基于我这样的设计,10个用户控件自己并不负责去建立数据库连接,而只是简单的告诉框架我要获取什么数据,框架会负责搜集搜索当前请求所需要的所有数据库请求,并一次性执行,并且只需要建立一次数据库连接即可。等所有请求处理完之后,再统一将所有的回复发给相应的控件。
删除版块功能的实现,通过Ajax技术实现,代码如下:
2 public void DeleteSection(int sectionId)
3 {
4 TRequest<Post> postRequest = new TRequest<Post>();
5 postRequest.Data.SectionId.Value = sectionId;
6
7 ThreadRequest threadRequest = new ThreadRequest();
8 threadRequest.Data.SectionId.Value = sectionId;
9
10 TRequest<SectionRoleUser> sectionRoleUserRequest = new TRequest<SectionRoleUser>();
11 sectionRoleUserRequest.Data.SectionId.Value = sectionId;
12
13 ProcessResult result = Engine.Executes(
14 BinderBuilder.BuildDeleteListBinder(postRequest),
15 BinderBuilder.BuildDeleteListBinder(threadRequest),
16 BinderBuilder.BuildDeleteListBinder(sectionRoleUserRequest),
17 BinderBuilder.BuildDeleteBinder<Section>(sectionId));
18
19 if (!result.IsSuccess)
20 {
21 throw new Exception(result.ErrorMessage);
22 }
23 }
该函数接受一个由Ajax框架传递过来的一个sectionId。首先交代一下一个论坛中一些基本表的关系:一个版块下包含多个帖子,一个帖子包含多个回复,另外一个版块还会有一些版主;
所以当我要删除一个版块时,还必须级联删除所有与之相关的帖子、回复、版主信息;而所有这些删除操作必须以事务的方式来执行,这样才可以确保数据完整性。
所以,上面的函数中我创建了四个请求,并全部传递给数据处理引擎Engine的Executes方法,该方法可以接受多个请求,并以事务的方式执行所有的请求;
BuildDeleteListBinder表示我要创建一个请求,该请求会删除符合指定条件下的所有记录;BuildDeleteBinder表示我要创建一个请求,该请求会根据一个主键删除一个实体,而实体的类型由泛型类型来告诉框架;
3.2 新增版块:
为了简单起见,我直接介绍新增版块的相关代码,而不写在前面已经介绍过的代码了。
2 public void Save()
3 {
4 CheckData();
5
6 Section section = new Section();
7 section.Subject.Value = subjectTextBox.Value;
8 section.Enabled.Value = enabledCheckBox.Checked ? 1 : 0;
9 section.GroupId.Value = int.Parse(groupDropDownList.Value);
10 Engine.Create(section);
11 }
12
13 private void CheckData()
14 {
15 if (string.IsNullOrEmpty(groupDropDownList.Value))
16 {
17 throw new Exception("请选择一个版块组!");
18 }
19
20 TRequest<Section> request = new TRequest<Section>();
21 request.Data.Subject.Value = subjectTextBox.Value.Trim();
22 if (Engine.GetAll(request).Count > 0)
23 {
24 throw new Exception("您要添加的版块已经存在!");
25 }
26 }
在添加版块时,需要检查名称是否重复,在CheckData函数中,通过调用Engine提供的GetAll方法来获取所有当前版块名称的记录,如果有,说明存在重复;如果不存在,则做保存操作。
可以清晰的看到,我先创建一个版块对象,然后调用Engine.Create方法完成版块的创建。
3.3 修改版块:
2 public void Save()
3 {
4 Section section = Engine.Get<Section>(new TRequest<Section>(GetValue<int>(ForumParameterName.SectionId)));
5
6 CheckData(section);
7
8 section.Enabled.Value = enabledCheckBox.Checked ? 1 : 0;
9 section.Subject.Value = subjectTextBox.Value;
10 Engine.Update(section);
11 }
12 private void CheckData(Section section)
13 {
14 if (section == null)
15 {
16 throw new Exception("版块组已经被删除!");
17 }
18 TRequest<Section> request = new TRequest<Section>();
19 request.Data.Subject.Value = subjectTextBox.Value.Trim();
20 EntityList sections = Engine.GetAll(request);
21 if (sections.Count > 0 && ((Section)sections[0]).EntityId.Value != section.EntityId.Value)
22 {
23 throw new Exception("新版块名称和已有的版块名称重复!");
24 }
25 }
在保存修改的版块时,首先通过Engine.Get<TEntity>方法获取当前正在编辑的版块,然后同样先检查版块名称是否合法,如果合法,
则调用Engine.Update方法保存版块;一切看起来都非常简单。
总结:
本文通过介绍一个论坛中版块的管理来简单介绍该如何使用我的框架所提供的实用功能。基于我提供的框架,当我们以后要开发一个应用模块时,只要先创建好表,然后做几个非常简单的ROM配置,然后就可以调用框架提供的接口来完成你想要的数据处理请求了。而你的所有数据处理请求都是创建Request,然后发送给框架,然后框架就会返回给你一个Reply,或者直接返回你想要的数据。也就是说,你以后都不必再写SQL了,也不需要写自己的数据访问层了。当然,现在很多ROM框架也都已经提供了非常强大的功能,让我们在开发应用时,可以不必去面向数据库字段,而是直接面向对象的属性。我开发这个ROM框架,也是希望能够提供给大家另外一种ROM的选择。当然我自知自己的这个东西还远不能和Hibernate,EF等大型成熟的框架相提并论,并且我还从未研究过他们的实现,呵呵。我只是希望通过自己的努力和积累,可以让我自己的开发很简单快捷而已。当然因为同时也希望能给大家分享我的成果,才发布给大家,并在这里写文章介绍这个东西。我觉得现在的我是多么的纯洁和可爱啊,应该说还没有被社会污染,哈哈。
好了,就这么多吧!这个框架的其他内容,如果大家有兴趣,就自行去研究吧,基本上所有的功能在我发布的蜘蛛侠论坛中都已经用到了。