引言
自从 NET 真正走入开发人员那天起效率两个字就一直成为众多程序员津津乐道的话题无论是从开发模式(Cross Language)系统框架(NET Framework)还是各种使用方便的工具(VSNET)无一不体现出了它的胜人一筹
同时在另一方面NET 是否可以真正胜任企业级应用(Enterprise Application)开发的重任却依然争论不断褒贬不一
通常来说对于一个企业级应用需要考虑的方面很多如安全性能伸缩性易用性等在本文中作者更愿意与大家一起探讨 NET 下数据访问层的相关技术这可能是在多层架构(nTier Architecture)诞生之日起就受到广泛关注的敏感话题而对于大部分开发人员来说这也可能是项目中最让人沮丧的部分甚或引起争议最多的部分
在以下论述中为统一起见作者暂时将数据访问层简称为DAL(Data Access Layer)
分析问题
简单统计分析后就不难发现DAL之所以让人畏惧并非出于技术本身的问题甚至恰恰相反很多开发人员认为这是最没有技术含量的部分之一(就作者经历的大小项目来看该层所占的开发时间一般较短也是很多开发人员不愿意承担的苦差)只是架构需要或者某些思想作怪(如为DAL而DAL)才加入了这所谓的第四层(传统三层架构并没有提出DAL思想)
DAL的提出确实对传统的架构模式提出了巨大挑战加入的目的肯定也是希望借其进一步提高生产效率在这种模式下理想情况是大部分开发人员从此摆脱DBA之苦甚或彻底断绝与数据库的直接关系SQL之痛将离我们而去整个OO世界从此清静
不过理想归理想能否成为现实则需通过项目检验
接下来作者试图分析比较流行且较有代表性的几种解决方案看看能否从中得出一些有价值的结论并为我们今后在设计与实现DAL时提供一些借鑒
ADONET
首先提到NET下的DAL立刻映入眼帘的就是ADONET
没错几乎所有的DAL解决方案(请允许作者使用Solution而非Framework)都必须从它发展而来没得选择这也是具有NET特色的实现方式(相比较JEE)
排除商业因素及CLR本身的需要ADONET真正带给我们的东西不多值得一提的也就DataSet(就作者经历的项目来说使用更多的是DataTable和DataView)从微软早期的内存数据库(Memory Database)鲜有人问津到今天的DataSet大行其道这其中的曲折实非片言只语所能道尽总之有一点可以肯定正是有了DataSet这种选择NET下的DAL才能象今天这般百花齐放大家的思路才能更趋开阔
Duwamish
这方面有很多好的Sample最经典的莫过于微软大力推荐的企业级开发套餐Duwamish对于希望学习NET下DAL设计的朋友这是一个不错的起点这方面的完整剖析大家可以参考CSDN开发高手本文不再赘述
作者自己参与的一个项目中就使用了Duwamish方案当时限于工期感觉这是一个很好的参考没做深入分析就开始设计了现在回想起来发现还是有很多不足之处
举个简单的例子Duwamish方案中并没有考虑Cache Management而这对于企业级应用来说某些时候就是一个不得不考虑的问题另一方面虽然Duwamish中告别了SQL语句(全部采用存储过程实现)但数据库痕迹依旧十分明显比如某些字段名称的定义关联表名称的定义等等
还有一个十分头疼的问题是在开发过程中体现出来的一开始那些比较简单的数据表还比较容易实现到了一些包含相互关系的数据表时我们的DAL工程师就感到了压力到后来几乎又做了一遍DBA在数据库建模时早已做过的工作只不过这次将数据库脚本换作了C# 实现(或者说将数据库结构换成了表面上具有OO特色的DataSet)而已
可能Duwamish的实现比较经典但在实际应用中有时并不意味着Best Practice就拿我们的项目来说虽然成功交付但无论从模型复用角度还是开发效率来说都不能算很成功套用一句流行语其实我们可以做得更好!
PetShop
ADONET上另一个值得参考的DAL实现就是鼎鼎大名的PetShop
当然了与Duwamish相似名气大未必真的实用PetShop虽然弥补了Duwamish在某些方面的不足例如通过Factory支持多种数据库存储引入了Cache机制提供了更为便利的SQL Helper但也同时带来了另一些问题其中最麻烦的就是SQL语句的引入而且还是针对不同数据库存储的不同SQL语句(主要是SQL Server与Oracle的参数表示方式不同)
另一方面PetShop虽然没有使用DataSet而代之以更为简洁的普通实体对象(Model)但它还是将DataReader的结果转换到了包含实体对象的列表集合中供离线使用从这个意义上说可谓换汤不换药甚至在某些场合例如需要进行数据过滤或者在主从数据间导航反而更为不便(此时简单的Collection或者List是无法满足需求的DBA与DAL开发人员只能再提供其它的方法来达到目的)
从上述两个例子中我们可以看出即使在微软的开发团队中也没有能够在DAL这个问题上达成一致这方面的更详细信息有兴趣的朋友可以参考如下文章
实战
上面剖析的两个解决方案让我们看到了它们各自的优势与不足而企业级应用的复杂环境也不太可能要求一个放之四海而皆准的框架就能解决所有难题因此只能根据具体情况具体分析作者曾经参与一个(NET)大型外包项目的开发工作有幸一睹其DAL的设计思想深感震撼在此与各位朋友一起共同探讨以SQL Server所带Northwind数据库为例如下就是一段基于该DAL的调用代码(作者做了一些名称上的调整)
// 根据EmployeeID返回其Title
boEmp = new EmployeeDAL();
boEmpKeys[Emp_ID] = ; // 注意实际字段名为EmployeeID
boEmpSelect();
string strTitle = boEmp[Emp_Title]; // 注意实际字段名为Title
……
// 根据 City 返回所有符合条件的 Employee
boEmp = new EmployeeDAL();
boEmpKeys[Emp_City] = Seattle;
boEmpSelect(); // 注意该方法与上面的调用完全相同
DataTable dtEmp = boEmpTable;
如果不考虑对象创建(可以采用Object Pooling或者Cached Object)以及调用后的处理实际的代码只有两行!
更让人吃惊的是上述EmployeeDAL类没有任何真正意义上的实现代码仅仅是声明了类名然后从一个通用基类继承而已!!
最优雅的地方还不在于此实际上就算在那个基类中也根本看不到SqlConnection或者OracleAdapter之类的帮派之争
相信大家也猜出来了没错它是借鑒了PetShop的实现采用了Factory模式来保证DAL可以适用于不同的数据库存储不过这种实现与PetShop还是有很大的区别至少它没有产生不同的SQL语句更没有出现不同的参数调用方式(SQL Server中一般使用@符号Oracle中一般使用符号)所有帮派一视同仁!
这其中当然得益于Factory的实现技巧但更重要的因素还在于设计方式的精妙其实在NET Framework中已经提供了这种设计方式的基石说白了就是SystemData中的那些Interface(如IDBConnectionIdataAdapter等)
在这样的设计基础上我们针对每一个DAL类就不再需要为不同的数据库存储提供不同的数据存取实现了例如在PetShop中针对订单数据需要实现Order类很自然的系统为SQL Server与Oracle分别实现了Order类并使用不同Provider(SqlClientOracleClient)提供的方法进行操作而在实际调用时PetShop通过Factory模式动态创建真正的Order类并激活相应的方法一个面向不同数据库存储的方案就跃然纸上
其实PetShop这种方案已经比较灵活了如果更能省去撰写不同Order类之苦那就真的送佛送到天了J而所有这些功能在作者所参与的这个项目中已经完全搞定了!
至于上面的EmployeeDAL(当然包括其它所有DAL类)没有任何真正实现代码只不过玩了一个小小的配置技巧而已将不同的DAL类与相关的Stored Procedure(请注意不是Table或View)按照Namespace分别存储到XML文件中
可能大家已经看出来了理论上甚至只需要一个DAL类就可以完成上述所有的工作!但在实际操作中不同的DAL类可能还是有一些数据处理上的细微差别(比如数据校验格式转换等)
总的来说在这样一个大项目中不可能要求所有开发人员(除了DBADAL Framework Developer)都去了解ADONET的方方面面虽然作者对此颇有研究但在这个项目中却从头至尾只用到了两个类DataTableDataView(甚至连Transaction都无需了解)!