1、演示地址: http://www.silks.cc/ 隔了很长时间,终于又有时间可以坐下来写写文章了。按照大纲的顺序,今天介绍整个实体引擎(EntityEngine)的设计,以及如何使用它来完成各种数据库操作。首先来看一下EntityEngine引擎中相关的几个关键的类。 图片看不清楚?请点击这里查看原图(大图)。 当用户要对数据库做某个操作时,需要先创建一个EntityRequest实例,然后将该实例传给 RequestBuilder,RequestBuilder会对该实例做进一步的一些通用的初始化操作,比如设置请求的类型。 EntityRequest创建完成后,
2、根据该EntityRequest实例进一步创建一个RequestBinder实例,该实例的作用是将当前的 EntityRequest实例和一个EntityReply实例绑定起来。然后框架就会把这个RequestBinder实例发送传给 EntityEngine引擎,EntityEngine引擎调用EntityManager来处理请求,而EntityManager则调用一个 EntityProvider实例来真正操作数据库。请求执行完成后,会更新EntityReply实例,EntityReply中包含了一个 Entity实例,这个Entity实例可以是任意数据,可以是单个实体,也可以是一个集合。
3、以上就是EntityEngine引擎的大致执行流程。 下面再明确列一下这几个类的主要职责或作用: EntityRequest:提供一个类,用来表示用户的请求,存放请求的一切相关数据; RequestBuilder:设置请求类型,或者根据一些参数来创建一个请求(EntityRequest); ----------------------------------------------------------2---------------------------------- RequestBinder:将请求和一个回复(EntityReply)捆绑,即建立对应关系;
4、 EntityEngine:发送一个请求,当然实际上发送的是RequestBinder; EntityManager:一个实体管理类,提供如CRUD等操作,它内部调用一个EntityProvider实例来完成真正的数据库操作; EntityProvider:它是一个Provider实例,提供了操作数据库的统一接口,该类会调用ADO.NET完成数据库的操作; EntityReply:表示请求的回复,一个EntityRequest对应一个EntityReply;EntityReply中包含了请求的所有结果信息; Entity:一个抽象类,一个应用中所有的真实实体类都继承
5、自它,它能表示单个实体对象,也能表示一个集合对象; EntityList:表示一个包含多个Entity的集合,但它也是集成自Entity的; 好了,了解了EntityEngine引擎的大致流程以及每个关键类的功能后,我想就可以举个简单的例子来说明具体应该如何来使用这些类。就以发表帖子这个功能来举例吧: 1. 在ThreadAdd.cs文件中,当用户点击发帖按钮后,会执行SaveThread方法,该函数首先创建一个Thread实例,即创建一个帖子实例,然后设置帖子的相关信息,然后调用BusinessManager.CreateThread方法来创建帖子到数据库。代码如下:
6、1private void SaveThread(object sender, EventArgs e) 2{ 3 //检查版块是否存在 4 if (section == null) 5 { 6 throw new Exception("请确认您所在的版块是存在的。"); 7 } 8 9 //获取当前帖子 10 Thread thread = new Thread(); 11 12 //设置帖子属性 13 thread.GroupId.Value = section.GroupId.Value
7、 14 thread.SectionId.Value = section.EntityId.Value; 15 thread.Subject.Value = subjectTextBox.Value; 16 thread.ThreadMarks.Value = int.Parse(threadMarksTextBox.Value); 17 thread.Body.Value = bodyEditor.Value; 18 thread.ThreadStatus.Value = (int)ThreadStatus.Normal; 19 thread
8、AuthorId.Value = CurrentUser.EntityId.Value; 20 thread.Author.Value = CurrentUser.NickName.Value == null ? "" : CurrentUser.NickName.Value; 21 thread.CreateDate.Value = DateTime.Now; 22 thread.UpdateDate.Value = DateTime.Now; 23 thread.StickDate.Value = DateTime.Parse("1753-01-01");
9、 24 thread.TotalViews.Value = 0; 25 26 //添加帖子 27 BusinessManager.CreateThread(thread); 28 29 //返回列表页面 30 Page.Response.Redirect(SiteUrls.Instance.GetThreadsUrl((int)ThreadOrderType.UpdateDate, 31 (int)ThreadStatus.Normal, (int)ThreadReleaseStatus.Open, section.EntityId.
10、Value)); 32} --------------------------------------3--------------------- 2. BusinessManager.cs文件: 1 private static EntityEngine entityEngine = new EntityEngine(); 2 public static void CreateThread(Thread thread) 3 { 4 entityEngine.ExecuteRequest(RequestHelper.BuildAddThreadRequest(thre
11、ad)); 5 } 该方法的实现非常简单,就是调用RequestHelper创建一个RequestBinder实例,然后直接调用一个静态的 EntityEngine实例的ExecuteRequest静态方法。前面说过,EntityEngine主要负责接收并发送 RequestBinder。接下来再看看RequestHelper是如何创建一个RequestBinder的? 3. RequestHelper.cs文件: 1 public static BaseRequestBinder BuildAddThreadRequest(Thread thread) 2 { 3
12、 return new TBaseRequestBinder
13、nder实例;下面在看看RequestBuilder的BuildAddEntityRequest做了什么呢?
4. RequestBuilder.cs文件:
1 public static TRequest BuildAddEntityRequest
14、Entity = entity; 6 return request; 7 } -----------4------------------- 由于用到了泛型,所以根据动态传入进来的类型创建一个EntityRequest实例,在这个例子中就是ThreadRequest。另外由于是新增的操作,所以还要再将该Request标记为Add,然后就是将要保存的Thread实例保存到EntityRequest中,以便告诉框架要保存的信息。好,现在对EntityRequest是如何创建出来的应该有所了解了,先看接下去看看这个Request是如何被发送和执行的。接下来转到 Enti
15、tyEngine的ExecuteRequest方法,该方法负责接收和发送Request。
5. EntityEngine.cs文件:
1 ///
16、Request方法发送Request。 6. RequestBinder.cs文件: 1 public override BaseReply ExecuteRequest() 2 { 3 return Request.Send(this); 4 } 前面分析过,一个RequestBinder会将一个Request和一个Reply进行捆绑。所以它自然会至少包含对这两个实例的引用。 而它的ExecuteRequest方法也非常简单,直接调用Request实例的Send方法去发送Request,当然发送时需要将 RequestBinder实例的引用传递进去,因为在E
17、ntityManager处理一个Request的时候,会把相应的返回信息保存到当前 Request对应的Reply中。下面看看EntityRequest是如何发送请求的。 7. EntityRequest.cs文件: 1 public override BaseReply Send(BaseRequestBinder requestBinder) 2 { 3 BaseReply reply = requestBinder.Reply; 4 SqlResult result; 5 6 switch (Operation) 7 {
18、 8 case OperationType.Get: 9 reply.Reply = EntityManager.GetEntity(requestBinder); 10 break; 11 case OperationType.Add: 12 result = EntityManager.CreateEntity(requestBinder); 13 reply.Status = result.Status; 14 repl
19、y.Log = result.Message; 15 break; 16 case OperationType.Update: 17 result = EntityManager.UpdateEntity(requestBinder); 18 reply.Status = result.Status; 19 reply.Log = result.Message; 20 break; 21 case OperationType.De
20、lete: 22 result = EntityManager.DeleteEntity(requestBinder); 23 reply.Status = result.Status; 24 reply.Log = result.Message; 25 break; 26 } 27 28 return reply; 29 } --------------5----------------- 由于用到了泛型,所以根据动态传入进来的类型创建一个EntityRe
21、quest实例,在这个例子中就是ThreadRequest。另外由于是新增的操作,所以还要再将该Request标记为Add,然后就是将要保存的Thread实例保存到EntityRequest中,以便告诉框架要保存的信息。好,现在对EntityRequest是如何创建出来的应该有所了解了,先看接下去看看这个Request是如何被发送和执行的。接下来转到 EntityEngine的ExecuteRequest方法,该方法负责接收和发送Request。
5. EntityEngine.cs文件:
1 ///
22、 3 /// 4 public BaseReply ExecuteRequest(BaseRequestBinder requestBinder) 5 { 6 return requestBinder.ExecuteRequest(); 7 } 该方法的实现非常简单,就是直接调用RequestBinder的ExecuteRequest方法发送Request。 6. RequestBinder.cs文件: 1 public override BaseReply ExecuteRequest() 2 { 3 retur
23、n Request.Send(this); 4 } 前面分析过,一个RequestBinder会将一个Request和一个Reply进行捆绑。所以它自然会至少包含对这两个实例的引用。 而它的ExecuteRequest方法也非常简单,直接调用Request实例的Send方法去发送Request,当然发送时需要将 RequestBinder实例的引用传递进去,因为在EntityManager处理一个Request的时候,会把相应的返回信息保存到当前 Request对应的Reply中。下面看看EntityRequest是如何发送请求的。 7. EntityReque
24、st.cs文件: 1 public override BaseReply Send(BaseRequestBinder requestBinder) 2 { 3 BaseReply reply = requestBinder.Reply; 4 SqlResult result; 5 6 switch (Operation) 7 { 8 case OperationType.Get: 9 reply.Reply = EntityManager.GetEntity(requestBin
25、der); 10 break; 11 case OperationType.Add: 12 result = EntityManager.CreateEntity(requestBinder); 13 reply.Status = result.Status; 14 reply.Log = result.Message; 15 break; 16 case OperationType.Update: 17
26、 result = EntityManager.UpdateEntity(requestBinder); 18 reply.Status = result.Status; 19 reply.Log = result.Message; 20 break; 21 case OperationType.Delete: 22 result = EntityManager.DeleteEntity(requestBinder); 23 reply.Statu
27、s = result.Status; 24 reply.Log = result.Message; 25 break; 26 } 27 28 return reply; 29 } -------------------------6---------------- 由于用到了泛型,所以根据动态传入进来的类型创建一个EntityRequest实例,在这个例子中就是ThreadRequest。另外由于是新增的操作,所以还要再将该Request标记为Add,然后就是将要保存的Thread实例保存到Entit
28、yRequest中,以便告诉框架要保存的信息。好,现在对EntityRequest是如何创建出来的应该有所了解了,先看接下去看看这个Request是如何被发送和执行的。接下来转到 EntityEngine的ExecuteRequest方法,该方法负责接收和发送Request。
5. EntityEngine.cs文件:
1 ///
29、{ 6 return requestBinder.ExecuteRequest(); 7 } 该方法的实现非常简单,就是直接调用RequestBinder的ExecuteRequest方法发送Request。 6. RequestBinder.cs文件: 1 public override BaseReply ExecuteRequest() 2 { 3 return Request.Send(this); 4 } 前面分析过,一个RequestBinder会将一个Request和一个Reply进行捆绑。所以它自然会至少包含对这两个实例的引用。
30、 而它的ExecuteRequest方法也非常简单,直接调用Request实例的Send方法去发送Request,当然发送时需要将 RequestBinder实例的引用传递进去,因为在EntityManager处理一个Request的时候,会把相应的返回信息保存到当前 Request对应的Reply中。下面看看EntityRequest是如何发送请求的。 7. EntityRequest.cs文件: 1 public override BaseReply Send(BaseRequestBinder requestBinder) 2 { 3 BaseReply re
31、ply = requestBinder.Reply; 4 SqlResult result; 5 6 switch (Operation) 7 { 8 case OperationType.Get: 9 reply.Reply = EntityManager.GetEntity(requestBinder); 10 break; 11 case OperationType.Add: 12 result = EntityManager.C
32、reateEntity(requestBinder); 13 reply.Status = result.Status; 14 reply.Log = result.Message; 15 break; 16 case OperationType.Update: 17 result = EntityManager.UpdateEntity(requestBinder); 18 reply.Status = result.Status; 19
33、 reply.Log = result.Message; 20 break; 21 case OperationType.Delete: 22 result = EntityManager.DeleteEntity(requestBinder); 23 reply.Status = result.Status; 24 reply.Log = result.Message; 25 break; 26 } 27 28
34、 return reply; 29 } ------------7------------------ 果然如此,这个函数的代码一目了然,就是一些简单的常用的ADO.NET操作的代码。首先实例化一个SqlCommand,然后设置该 SqlCommand的参数信息,然后执行该SqlCommand,然后将Output的参数设置到EntityReply中,如果有的话。最后关闭数据库连接。 好,到这里位置,当前请求算是执行完成了,接下来看看如何获取Reply,以及如何判断当前请求执行是否成功。因为当前这个例子是发表帖子,所以我们优势常常不会去判断是否成功。因为如果默认没有
35、发生异常的话,就简单的认为已经成功了。当然如果你真的想获取这些信息的话,就从Reply中去获取吧。下面再来看看最初发表帖子时的代码: 1 private void SaveThread(object sender, EventArgs e) 2 { 3 //检查版块是否存在 4 if (section == null) 5 { 6 throw new Exception("请确认您所在的版块是存在的。"); 7 } 8 9 //获取当前帖子 10 Thread thread = new Th
36、read(); 11 12 //设置帖子属性 13 thread.GroupId.Value = section.GroupId.Value; 14 thread.SectionId.Value = section.EntityId.Value; 15 thread.Subject.Value = subjectTextBox.Value; 16 thread.ThreadMarks.Value = int.Parse(threadMarksTextBox.Value); 17 thread.Body.Value = bodyEd
37、itor.Value; 18 thread.ThreadStatus.Value = (int)ThreadStatus.Normal; 19 thread.AuthorId.Value = CurrentUser.EntityId.Value; 20 thread.Author.Value = CurrentUser.NickName.Value == null ? "" : CurrentUser.NickName.Value; 21 thread.CreateDate.Value = DateTime.Now; 22 thread.Upd
38、ateDate.Value = DateTime.Now; 23 thread.StickDate.Value = DateTime.Parse("1753-01-01"); 24 thread.TotalViews.Value = 0; 25 26 //添加帖子 27 BusinessManager.CreateThread(thread); 28 29 //返回列表页面 30 Page.Response.Redirect(SiteUrls.Instance.GetThreadsUrl((int)ThreadOrderTyp
39、e.UpdateDate, 31 (int)ThreadStatus.Normal, (int)ThreadReleaseStatus.Open, section.EntityId.Value)); 32 } ----------------8------------------ 可以看到,我在调用BusinessManager的CreateThread方法创建帖子的时候,并没有关心其返回值,我这里偷懒了,我想真正的网站不应该如此,应该如下面这样: 1 private void SaveThread(object sender, EventArgs e)
40、 2 { 3 //检查版块是否存在 4 if (section == null) 5 { 6 throw new Exception("请确认您所在的版块是存在的。"); 7 } 8 9 //获取当前帖子 10 Thread thread = new Thread(); 11 12 //设置帖子属性 13 thread.GroupId.Value = section.GroupId.Value; 14 thread.SectionId.Value = secti
41、on.EntityId.Value; 15 thread.Subject.Value = subjectTextBox.Value; 16 thread.ThreadMarks.Value = int.Parse(threadMarksTextBox.Value); 17 thread.Body.Value = bodyEditor.Value; 18 thread.ThreadStatus.Value = (int)ThreadStatus.Normal; 19 thread.AuthorId.Value = CurrentUser.Enti
42、tyId.Value; 20 thread.Author.Value = CurrentUser.NickName.Value == null ? "" : CurrentUser.NickName.Value; 21 thread.CreateDate.Value = DateTime.Now; 22 thread.UpdateDate.Value = DateTime.Now; 23 thread.StickDate.Value = DateTime.Parse("1753-01-01"); 24 thread.TotalViews.Val
43、ue = 0; 25 26 //添加帖子 27 BaseReply reply = BusinessManager.CreateThread(thread); 28 29 if (reply.Status != 0) 30 { 31 //这里,我们可以将错误信息做任何处理,如保存到日志或显示给用户。 32 throw new Exception(reply.Log); 33 } 34 else 35 { 36 //返回列表页面 37 Page.R
44、esponse.Redirect(SiteUrls.Instance.GetThreadsUrl((int)ThreadOrderType.UpdateDate, 38 (int)ThreadStatus.Normal, (int)ThreadReleaseStatus.Open, section.EntityId.Value)); 39 } 40 } 好了,真个发表帖子的流程分析完了。各位有什么体会呢?是不是觉得为什么要搞出这么多层次结构的类,为什么不直接调用EntityProvider 来实现功能呢?呵呵,当然如果你要这样做也可以,除非你愿意一直
45、不断重复并且容易出错的写ADO.NET相关的代码。你可以发现,基于这一套框架,你做任何操作,都不必在代码中写SQL语句,你所要做的,仅仅是定义一些非常简单直观的实体类、定义好ORMapping配置文件、定义这些实体类相关的 Request,然后再写几个业务相关的接口,这样,整个数据访问层就好了。 其实,添加、删除、修改,以及根据主键去获取单个实体,这些操作都是非常简单的。我觉得最复杂的是多条件组合查询,或者是多个请求组合起来一起执行的情况。如果我一次性全部讲清楚可能文章会非常长,所以我准备留到下面几篇,重点介绍其他很多细节但又非常关键,或者说最能体现这个实用类库价值的一些设计。






