剖析 .Net 下的数据访问层技术(一)


l ** 引言 ** ** **

自从 .NET 真正走入开发人员那天起,“效率”两个字就一直成为众多程序员津津乐道的话题。无论是从开发模式( Cross Language )、系统框架( .NET Framework ),还是各种使用方便的工具( VS.NET ),无一不体现出了它的胜人一筹。

同时,在另一方面, .NET 是否可以真正胜任企业级应用( Enterprise Application )开发的重任,却依然争论不断,褒贬不一。

通常来说,对于一个企业级应用,需要考虑的方面很多,如安全、性能、伸缩性、易用性等。在本文中,作者更愿意与大家一起探讨 .NET 下数据访问层的相关技术,这可能是在多层架构( n-Tier Architecture )诞生之日起就受到广泛关注的敏感话题,而对于大部分开发人员来说,这也可能是项目中最让人沮丧的部分,甚或引起争议最多的部分。

在以下论述中,为统一起见,作者暂时将数据访问层简称为 ** DAL ** ( ** Data Access Layer ** )。

l ** 分析问题 ** ** **

简单统计分析后,就不难发现, DAL 之所以让人畏惧,并非出于技术本身的问题,甚至恰恰相反,很多开发人员认为这是最没有技术含量的部分之一(就作者经历的大小项目来看,该层所占的开发时间一般较短,也是很多开发人员不愿意承担的“苦差”),只是架构需要或者某些思想作怪(如:为 DAL 而 DAL )才加入了这所谓的 第四层 (传统三层架构并没有提出 DAL 思想)。

DAL 的提出,确实对传统的架构模式提出了巨大挑战,加入的目的肯定也是希望借其进一步提高生产效率,在这种模式下,理想情况是:大部分开发人员从此摆脱 DBA 之苦,甚或彻底断绝与数据库的直接关系, SQL 之痛将离我们而去,整个 OO 世界从此清静。

不过,理想归理想,能否成为现实则需通过项目检验。

接下来,作者试图分析比较流行且较有代表性的几种解决方案,看看能否从中得出一些有价值的结论,并为我们今后在设计与实现 DAL 时提供一些借鉴。

u ** ADO.NET **

首先,提到 .NET 下的 DAL ,立刻映入眼帘的就是 ADO.NET 。

没错,几乎所有的 DAL ** 解决方案 ** (请允许作者使用 Solution 而非

Framework )都必须从它发展而来,没得选择,这也是具有 .NET 特色的

实现方式(相比较 J2EE )。

排除商业因素及 CLR 本身的需要, ADO.NET 真正带给我们的东西

不多 , 值得一提的也就 DataSet (就作者经历的项目来说 , 使用更多的是

DataTable 和 DataView )。从微软早期的内存数据库( Memory Database )

鲜有人问津到今天的 DataSet 大行其道,这其中的曲折实非片言只语所

能道尽,总之,有一点可以肯定,正是有了 DataSet 这种选择, .NET 下

的 DAL 才能象今天这般百花齐放,大家的思路才能更趋开阔。

** Duwamish **

这方面有很多好的 Sample ,最经典的莫过于微软大力推荐的企业级开发套餐: Duwamish 。

对于希望学习 .NET 下 DAL 设计的朋友,这是一个不错的起点,这

方面的完整剖析,大家可以参考 ** “ ** ** CSDN ** ** 开发高手, ** ** 2003.11 ** ** ” ** ,本文不

再赘述。

作者自己参与的一个项目中就使用了 Duwamish 方案,当时限于工

期,感觉这是一个很好的参考,没做深入分析就开始设计了。现在回想

起来,发现还是有很多不足之处。

举个简单的例子, Duwamish 方案中并没有考虑 Cache Management ,

而这对于企业级应用来说,某些时候就是一个不得不考虑的问题;另一

方面,虽然 Duwamish 中告别了 SQL 语句(全部采用存储过程实现),

但数据库痕迹依旧十分明显,比如:某些字段名称的定义,关联表名称

的定义等等。

还有一个十分头疼的问题是在开发过程中体现出来的。一开始,那

些比较简单的数据表还比较容易实现,到了一些包含相互关系的数据表

时,我们的 DAL 工程师就感到了压力,到后来,几乎又做了一遍 DBA

在数据库建模时早已做过的工作,只不过,这次将数据库脚本换作了

C# 实现(或者说:将数据库结构换成了 表面上 具有 ** OO ** ** 特色 ** 的 DataSet )

而已。

可能, Duwamish 的实现比较经典,但在实际应用中,有时并不意

味着 Best Practice 。就拿我们的项目来说,虽然成功交付,但无论从模

型复用角度,还是开发效率来说,都不能算很成功。套用一句流行语:

其实我们可以做得更好!

** PetShop **

ADO.NET 上另一个值得参考的 DAL 实现就是鼎鼎大名的 PetShop 。

当然了,与 Duwamish 相似,名气大未必真的实用。 PetShop 虽然

弥补了 Duwamish 在某些方面的不足,例如:通过 Factory 支持多种数

据库存储,引入了 Cache 机制,提供了更为便利的 SQL Helper ,但也同

时带来了另一些问题。其中,最麻烦的就是 SQL 语句的引入,而且还

是针对不同数据库存储的不同 SQL 语句(主要是 SQL Server 与 Oracle

的参数表示方式不同)。

另一方面, PetShop 虽然没有使用 DataSet 而代之以更为简洁的普通

实体对象( Model ),但它还是将 DataReader 的结果转换到了包含实体对

象的列表集合中供离线使用,从这个意义上说,可谓 换汤不换药 。甚至,

在某些场合,例如:需要进行数据过滤,或者在主从数据间导航,反而

更为不便(此时,简单的 Collection 或者 List 是无法满足需求的, DBA

与 DAL 开发人员只能再提供其它的方法来达到目的)。

从上述两个例子中,我们可以看出,即使在微软的开发团队中,也

没有能够在 DAL 这个问题上达成一致。这方面的更详细信息,有兴趣

的朋友可以参考如下文章:

_ http://www.microsoft.com/china/community/Column/67.mspx _ 。

** 实战 ** ** **

上面剖析的两个解决方案,让我们看到了它们各自的优势与不足,而企业级应用的复杂环境也不太可能要求一个放之四海而皆准的框架就能解决所有难题,因此,只能根据具体情况具体分析。

作者曾经参与一个( .NET )大型外包项目的开发工作,有幸一睹其 DAL 的设计思想,深感震撼,在此与各位朋友一起共同探讨。

以 SQL Server 所带 Northwind 数据库为例,如下就是一段基于该 DAL 的调用代码(作者做了一些名称上的调整):

_ // _ _ 根据 _ _ EmployeeID _ _ 返回其 _ _ Title _

boEmp = new EmployeeDAL();

boEmp.Keys[“Emp_ID”] = 1; **_ // _ ** **_ 注意:实际字段名为: EmployeeID _ **

boEmp.Select();

string strTitle = boEmp[“Emp_Title”]; **_ // _ ** **_ 注意:实际字段名为: Title _ **

……

_ // _ _ 根据 _ _ City _ _ 返回所有符合条件的 _ _ Employee _

boEmp = new EmployeeDAL();

boEmp.Keys[“Emp_City”] = “Seattle”;

boEmp.Select(); **_ // _ ** **_ 注意:该方法与上面的调用完全相同 _ **

DataTable dtEmp = boEmp.Table;

如果不考虑对象创建(可以采用 Object Pooling 或者 Cached Object )以及调用后的处理,实际的代码只有两行!

更让人吃惊的是,上述 EmployeeDAL 类没有任何真正意义上的实现代码,仅仅是声明了类名,然后从一个通用基类继承而已!!

最优雅的地方还不在于此,实际上,就算在那个基类中,也根本看不到 SqlConnection 或者 OracleAdapter 之类的帮派之争。

相信大家也猜出来了,没错,它是借鉴了 PetShop 的实现,采用了 Factory 模式来保证 DAL 可以适用于不同的数据库存储。不过,这种实现与 PetShop 还是有很大的区别:至少,它没有产生不同的 SQL 语句,更没有出现不同的参数调用方式( SQL Server 中一般使用“ @ ”符号, Oracle 中一般使用“:”符号),所有帮派一视同仁!

这其中,当然得益于 Factory 的实现技巧,但更重要的因素还在于设计方式的精妙。其实,在 .NET Framework 中,已经提供了这种设计方式的基石,说白了,就是 System.Data 中的那些 Interface (如: IDBConnection , IdataAdapter 等)。

在这样的设计基础上,我们针对每一个 DAL 类,就不再需要为不同的数据库存储提供不同的数据存取实现了。例如:在 PetShop 中,针对订单数据需要实现 Order 类,很自然的,系统为 SQL Server 与 Oracle 分别实现了 Order 类并使用不同 Provider ( SqlClient , OracleClient )提供的方法进行操作。而在实际调用时, PetShop 通过 Factory 模式动态创建真正的 Order 类并激活相应的方法,一个面向不同数据库存储的方案就跃然纸上。

其实, PetShop 这种方案已经比较灵活了,如果更能省去“撰写不同 Order 类”之苦,那就真的送佛送到天了 J 。而所有这些功能,在作者所参与的这个项目中,已经完全搞定了!

至于上面的“ EmployeeDAL (当然,包括其它所有 DAL 类)没有任何真正实现代码”,只不过玩了一个小小的配置技巧而已:将不同的 DAL 类与相关的 Stored Procedure (请注意:不是 Table 或 View )按照 Namespace 分别存储到 XML 文件中。

可能大家已经看出来了,理论上,甚至只需要一个 DAL 类就可以完成上述所有的工作!但在实际操作中,不同的 DAL 类可能还是有一些数据处理上的细微差别(比如:数据校验,格式转换等)。

总的来说,在这样一个大项目中,不可能要求所有开发人员(除了 DBA <SPAN style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family:

Published At
Categories with Web编程
Tagged with
comments powered by Disqus