Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1150667
  • 博文数量: 141
  • 博客积分: 3161
  • 博客等级: 中校
  • 技术积分: 3011
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-27 14:53
文章存档

2012年(28)

2011年(113)

分类:

2011-09-29 09:16:25

.NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(前篇)

   前言:这个系列有段时间没有动了。主要是针对大家的反馈在修改代码。在修改的过程中,也有了一些新的体会,这里和大家分享一下,同时也发布一下业务框架 的第一个版本。在本篇文章中,学习到的不是仅仅只是代码,而是设计的思想和实现这种思想的方法。在写本篇时有个感触:把一个东西彻底的讲清楚,不容易。希 望大家

多提意见。而且在写本篇的时候,我个人也是很兴奋的,至于原因相信大家在看完之后就知道了。J

本篇的议题如下:

1.      打通业务层和数据层

2.      打通方法的选择和实现

3.      再次借鉴.NET Framework设计思想

4.      水到渠成

5.      代码的版本说明


1.    打通业务层和数据层

首先,回顾之前的文章一直讨论的问题:

1. 如何使得数据层”以不变应万变”。

2. 条件对象如何实现

3. 如何在业务层和数据层之间mapping数据

本篇就告诉大家,如何切切实实的解决上面三个问题。

 

首先,从一个图示开始讲述。

 

 

从上面的图中可以看出,架起在BLL和DAL之前的桥梁的就是中间的那个条件对象。正是因为有了这个,所以上面的提出的问题才得以解决。

下面先从操作上总体来讲述一下这个图的具体流程:

a.       在业务类中创建一个方法。例如在业务类User中,定义如下:

现在只看GetUserByAge这个方法:在方法中构造出一个条件对象,大家第一眼能看出来:是Linq的实现。其实最后的实现只是借用了Linq的思想,仅此而已

b.       解析条件对象。 在上面的构造的条件对象中,Age,Name等,都是业务类User的字段,这些字段的值肯定最终是从数据库中的表字段中获取的(或者是通过数据库中的值 算出来的),所以在解析条件对象的时候,就要知道这些业务属性对应数据库中哪个表的哪个字段。因此在业务类中声明每个属性的时候就要同时保留它所对应的数 据库字段的信息。一旦业务类的属性中保存了这些信息

c.       数据层操作SQL语句。在解析条件对象的时候,就会最终得到相对应的SQL语句,然后在数据层中执行这些SQL语句。

可能上面讲的比较的抽象,因为本篇的要讲述的东西确实比较的多(Word中写了超过了10页),上面讲述的三个步骤也是先按大家有个印象。不是很懂也没有关系。

 

 2. 打通方法的选择和实现

       接下来就是方法的探索和思考,以及实现的过程。我是想带着个大家跟着一起看看,为什么最后会采用这个解决方案的。

首先,就从条件对象开始看起。

在 实现条件对象(条件对象和查询对象的区别之前讲过,这里重述一下:查询对象只是条件对象的一个子集,查询对象用来在查询的使用构造查询条件;条件对象不仅 仅在查询时构造条件,而且在增加,删除,,修改时候也使用,例如:只修改Name=”admin”的数据,在修改数据库的时候也用了一定的条件。所以条件 对象>查询对象)的时候,也是参看了其他开源框架的一些实现(Nhibernate中查询对象的,CSLA)。

同时要明白一点:设计出来的框架是给开发人员使用的,所以要考虑如何使得开发人员最快最好的使用框架,所以要从开发人员的角度看(这一点也是很重要的)。例如在Nhibernate中查询对象,使用的方法如下(仅仅是简单的举例而已):

代码
其实本系列中的业务框架之前的条件对象的构造也是参看了Nhibernate中查询对象的方法来实现和使用的,如下:

 

ICriteria condition=CriteriaFactory.Create(typeof(ProductBL).Where("ProductName", Operation.Equal,"book");

 因为现在.NET中的开发人员对Linq的一些操作比较的熟悉,而且如果把条件对象的使用方式改为下面的方式:

ICriteria<ProductBL> condition=CriteriaFactory.Create<ProductBL>(o => o.ProductName == "book");

   

  那么开发人员的学习成本就几乎为零(因为他们熟悉Linq,如果条件对象也采用这种比较统一的方法实现,他们就可以采用”以此类推”的思想来使用框架),更多的好处我就不说了,大家可以自己体会或者参看本系列之前的文章。

 

接下来就探索实现条件对象的方法(Linq to XXX篇)

熟悉Linq的朋友可以看出:可以使条件对象实现IQueryable接口,然后采用实现linq to XXX的方法,实现自己的Linq Provider。这个方法是否可行,下面,我们就深入linq to XXX的来看一看,看完之后,结果就很清楚了。

首先来看下面两个接口: 

代码

  

Linq的 出现,使得很多东西都和linq扯上了关系:Linq to sql, Linq to Google, Linq to javascript, Linq to Nhibernate...... 所列出来的这些,我们统称为linq to XXX。这些Linq to XXX都是实现了上面两个接口。

下面就通过自己实现linq to sql来举例分析。

从总体来看:linq to sql的本质就是:把操作转换为sql语句,然后用ADO.NET执行,最后把结果转换为实体返回。

其实下面列出了这么多的代码,其中最关键的其实就是QueryProvider中的Execute方法:这个方法负责把你的操作进行解析,其实真正负责解析的QueryTranslator。

 

代码

 

 

代码

 

 

 

代码

 

 

 

对 于实现了IQueryable的对象,在他们进行的每一个操作(也就是调用实现这个接口的类上的方法)其实都没有立刻去执行,而且把进行的操作记录下来 了,放在一个称为Expression Tree表达式数的数据结构中,然后再真正执行的时候(就是调用Execute来执行), QueryTranslator对象就遍历表达式树,对操作进行解析,例如linq to sql就是把表达式树中操作解析为对数据库进行的操作,以sql语句的形式体现出来.

 

       当把操作解析为了sql语句之后,就是用ADO.NET的方法来执行SQL操作,然后通过反射,把ADO.NET执行的结果转换为数据实体。如下:       

代码

 

 

       上面简单的介绍了如何实现linq to sql具体的实现代码,大家可以自己过后慢慢的看或者参看我的另外的一个linq系列,现在我们继续后面的话题。 

       现在我们回到之前的话题:条件对象是否可以采用这种方式实现      

       大 家看到上面的代码Query其中的T,和Execute方法的返回值。在上面的代码中,如果T是User类, 即,Query,那么最后的Execute方法返回的一定会是User的集合。也就是说:Execute方法已经对数据源(这里 是数据库)进行了操作,并且把结果以数据实体的形式已经返回了,并且返回的数据实体的类型就是T,例如User。

       但是我们这里的要实现的条件对象只是想把条件构造出来,不是立刻去执行。至于具体的执行数据操作者(DataProvider)可以任意选择的:使用ADO.NET方法,还是EF的方法,还是Nhibernate,都是可以配置的。如下:      

代码

       

   上面的代码中,Create方法就是实例化一个ICriteria,此时我们想做的仅仅只是一件事:把在 ICriteria上的操作记录下来而已。然后把记录下来的结果解析,解析的最终结果就是一条sql命令,然后再给不同的 DataProvider去执行。也就是说,在DataPortal内部可以配置用什么方法来执行数据操作:是直接使用ADO.NET执行sql命令,还 是把sql命令给Entity Framework...通过配置决定。如果ICriteria是从IQueryable接口进行了继承,那么在ICriteria实 现这个结果的过程中就必须要去数据库中进行执行,因为Execute方法返回的是T的集合,而不是sql命令(字符串)。 

       大 家可能想到:那就在Execute方法中去实现不同的DataProvider,例如之前的例子在ObjectReader用ADO.NET实现了,那么 也可以在ObjectReader中用EF实现数据操作。这个方法确实可以,也很不错。但是这个方法在分布式开发中(特别是在WCF中)有一点的局限性。 例如你有一个界面,上面可以有很多的选项,如下:

 

 

  在服务接口那边,你肯定不想定义N多差不多的接口方法:如

GetUserByName(string username);

GetUserByEmail(
string email);

 

  或者

GetUserByCondition(string username,string password,string email .....);

 

   这样都是很不灵活的,如果User的属性减少了或者增多了,那么如果要在服务器那边暴露的接口的方法也要修改,这样终究是不好。如下采用下面的方法: 

GetUserByCondition(Critera condition);

  

   其中,Critera是条件对象。那么我们在客户端就可以任意构造条件对象,这个条件对象就把在它上面进行的操作记录下来,然后统一的交给 GetUserByCondition方法去服务器解释并执行。此时,这个条件对象就是在客户端生成的,而且这个条件对象此时是不用去数据库中去执行的。 如果条件对象是从IQueryable接口继承的,那么在客户端构造完条件对象之后,就要去数据库中执行了,如果再在ObjectReader搞个分布式 调用,难度不说,也很别扭,这不是我们所要的。

       所以,综合上面的一些考虑,那么可以确定:条件对象不继承IQueryable接口。但是我们又希望采用类似linq的操作,那么只有自己实现了。 

  本篇就暂时写到这里,因为太长了,所以分为前篇和后篇发布,因为博客园不能在一小时内发两篇,所以后篇将会在9点左右发布。希望大家见谅。

  版权为和博客园所有,,欢迎转载,转载请标明出处给作者。

   http://www.cnblogs.com/yanyangtian

阅读(1299) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~