日期:2012-03-11  浏览次数:20597 次

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的实现,采用了Fact