全部博文(141)
分类:
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中查询对象,使用的方法如下(仅仅是简单的举例而已):
因为现在.NET中的开发人员对Linq的一些操作比较的熟悉,而且如果把条件对象的使用方式改为下面的方式:
那么开发人员的学习成本就几乎为零(因为他们熟悉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
但是我们这里的要实现的条件对象只是想把条件构造出来,不是立刻去执行。至于具体的执行数据操作者(DataProvider)可以任意选择的:使用ADO.NET方法,还是EF的方法,还是Nhibernate,都是可以配置的。如下:
上面的代码中,Create方法就是实例化一个ICriteria
大 家可能想到:那就在Execute方法中去实现不同的DataProvider,例如之前的例子在ObjectReader用ADO.NET实现了,那么 也可以在ObjectReader中用EF实现数据操作。这个方法确实可以,也很不错。但是这个方法在分布式开发中(特别是在WCF中)有一点的局限性。 例如你有一个界面,上面可以有很多的选项,如下:
在服务接口那边,你肯定不想定义N多差不多的接口方法:如
或者
这样都是很不灵活的,如果User的属性减少了或者增多了,那么如果要在服务器那边暴露的接口的方法也要修改,这样终究是不好。如下采用下面的方法:
其中,Critera是条件对象。那么我们在客户端就可以任意构造条件对象,这个条件对象就把在它上面进行的操作记录下来,然后统一的交给 GetUserByCondition方法去服务器解释并执行。此时,这个条件对象就是在客户端生成的,而且这个条件对象此时是不用去数据库中去执行的。 如果条件对象是从IQueryable接口继承的,那么在客户端构造完条件对象之后,就要去数据库中执行了,如果再在ObjectReader搞个分布式 调用,难度不说,也很别扭,这不是我们所要的。
所以,综合上面的一些考虑,那么可以确定:条件对象不继承IQueryable接口。但是我们又希望采用类似linq的操作,那么只有自己实现了。
本篇就暂时写到这里,因为太长了,所以分为前篇和后篇发布,因为博客园不能在一小时内发两篇,所以后篇将会在9点左右发布。希望大家见谅。
版权为和博客园所有,,欢迎转载,转载请标明出处给作者。