剖析 .Net 下的数据访问层技术 Posted by Admin on 2004年4月27日 (中) 自从 .NET 真正走入开发人员那天起,“效率”两个字就一直成为众多程序员津津乐道的话题。无论是从开发模式(Cross Language)、系统框架(.NET Framework),还是各种使用方便的工具(VS.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,DAL Framework Developer)都去了解ADO.NET的方方面面,虽然作者对此颇有研究,但在这个项目中,却从头至尾只用到了两个类:DataTable,DataView(甚至连Transaction都无需了解)! 其它 结束ADO.NET剖析前,不得不提提DataReader与DataSet间的兄弟 之争。 就作者所看过的资料,几乎所有的都建议实际情况具体分析,剩下 很少很少的则全凭个人习惯决定。 在学习ADO.NET时,作者也是抱着这样的想法,并反复牢记资料 上总结的那些条款(就像当年学习GOF 23条时那样,几乎可以倒背如 流了J),想到终有一日也可在ADO.NET下大展神威了。
可惜现实不随人愿,连续做了几个项目,无论规模大小,竟然全部 采用了DataSet解决方案! 此时,再回头看看学习ADO.NET时打开最为频繁的PetShop项目, 两相一比较,这才看出些许端倪。
简单的说,PetShop采用了如下这种“曲线救国”的方式来实现数据 交换:
DataReader获取数据 => 创建数据实体类 => 根据字段类型填充数 据实体类 => 将数据实体添加到列表类中(仅针对返回超过一条数据的 场合) (补充:采用数据实体类或者集合类可以比较方便的实现Cache Manament, 而普通的DataReader由于其数据读取方式限制,无法满足这种需求)
这个过程与DataAdapter.Fill() 所所产生的效果大同小异,只不过, 在Fill() 中DataAdpater自动创建DataReader去获取数据,之后创建 DataTable(相当于数据实体类),并根据字段类型填充DataTable,当然 ,如果可能返回多条记录,DataTable完全可以处理,就没必要去实现列 表操作了。 可能读者马上产生了疑问:既然如此,PetShop中为何还需要数据实 体类呢? 这其中还是有一些差别的。 首先,数据实体类是轻量级的structure,一般仅包含数据字段,没有 什么操作方法,这比DataTable或者DataRow还是有一些性能上的优势 (在数据量不大时可以忽略不计);另一方面,数据实体类的操作相对 简单,不需要开发人员具备任何ADO.NET知识(其实就DataTable来 说,这也不算什么问题),点点属性就可以了。 不过,根据作者的实践来看,这两方面似乎还不足以使人转而使用 DataReader方案,理由列举如下:
(1) 对于数据量较大的场合,可以采用分批读取的方式,这有点类似DataGrid的数据分页效果;
(2) 对于简单的数据,实体类还能应付,一旦涉及关联数据,就只能另外撰写方法了。而所有这些,在DataSet中是非常容易处理的(对于企业级应用,大部分情况都需要处理比较复杂的数据);
(3) DataTable“天生”就支持数据集合操作,这样的特性比“集合+实体”的混合模式(PetShop)更容易控制,也更自然;
(4) 实体类在声明时需要确定所有数据类型,当进行数据填充时,就需要DataReader再次关注实体所对应的数据类型,不能有丝毫差错!在这方面,DataTable就显得非常方便,操作时只需要一次类型关注即可;
(5) DataSet解决方案可以非常方便的支持序列化操作(如:Remoting,WebServices),同时,与XML的关系更是亲密无比,这对于和其它系统的交互来说也是至关重要的。
分析过一些技术和方案,相信读者朋友已有一些体会。值此收官之际,如果非要在这里提供一个“综上所述”,那作者的建议就非常明确:
在企业级应用开发中,尽可能的采用DataSet(DataTable / DataView)+ Cache Management解决方案!
其它开发中,只在如下4种情况才考虑使用DataReader(就作者经验来说,大部分使用DataReader都属第2种情况):
(1) 对资源要求比较苛刻的场合,这里的资源主要指内存和数据库连接;
(2) 希望在读取数据库返回结果集时作自定义处理,例如:在读取一条记录后立刻终止处理,或者在读取时作计算操作。 (提示:这种情况类似于XML中的SAX(Simple API for XML)技术,无需一次性读入所有XML数据即可进行操作;相反的,DOM(Document Object Model)则要求必须装载所有XML数据后才能开始操作(MSXML4.0已开始允许只读取XML文档部分数据即可开始操作,这是后话)!)
(3) 只希望得到返回记录数或者返回记录的部分字段,如: string GetNameByID(int nID) //根据员工ID返回员工姓名,这里只需要 // 读取姓名字段; (提示:这种情况一般可以通过执行特定的查询或存储过程直接解决)
(4) 出于某些方面的考虑(例如:n-Tier系统中严格区分各Layer间的职责),无法(或者禁止)通过数据库本身进行查询过滤,这时就只有使用DataReader在读取时进行过滤操作! (提示:虽然DataView也能达到这种目的,但它的过滤前提是必须读取到所有返回数据,所以性能上不如DataReader!)
u O/R Mapping O/R Mapping的全称是:Object Relational Mapping,主要目的是在传统RDBMS与OO Language之间建映射关系,从而使开发人员彻底脱离数据持久这片剪不断理还乱的苦海。 关于O/R Mapping或者近来比较热门的O/X Mapping(大家可以参考“程序员,2004.01,P86”),可能需要专门的文章进行详细论述,本文的目的主要是对现有方案的优缺点进行简单剖析以及提供一些实践中的参考信息。 相比较J2EE平台,.NET下的O/R Mapping可谓没什么历史,至今还尚未有经过考验的成熟的可用方案。但是,随着各大厂商的重视以及开源项目的如火如荼,.NET O/R Mapping的步伐也开始慢慢跟上,使这块本属于J2EE的领地加入了新的竞争对手(会不会使更多的开发人员投入.NET阵营?J),也让众多疲于在SQL Clause或ADO.NET中来回奔命的DAL开发人员看到了“光明之路”。
接下来,就让我们一起看看在这片比ADO.NET更广阔的土地上有些什么值得探讨的Solution。 ? Open Source 开源方面一直与.NET保持一定距离,O/R Mapping更是寥寥无几,但就作者的下载试用和源码分析来看,个人以为如下的两个解决方案还是有一定参考价值的:OPF,OJB。 有关这两个开源项目的简介,大家可以参考“程序员,2004.01,P13”。 OPF的全称是:Object Persistent Framework。 OJB的全称是:ObJect Relational Bridge。
在实现手法上,这两个方案的思路完全不同,具有各自的代表性。 OPF走的路线有点类似于Typed DataSet或Borland ECO(请参考下面的介绍),实现比较简单,提供更多的源码级控制;而OJB的实现则类似于Microsoft ObjectSpaces(请参考下面的介绍),采用了配置文件的方式,相对就比较复杂了。
这两个方案的基本框架如下所示: OPF:
从图中不难看出: (1) Persistent类扮演了DataSet的角色,除了常规的对象数据操作外,还可以设定不同对象间的关系(如主从关系,集合关系等,这一点在Borland ECO所生成的代码中也可略见一二),这也是上文所说“提供更多源码级控制”的原因所在;
(2) PersistentSqlDataManager则扮演了DataAdapter的角色,通过预先设置的Commands来执行真正的数据库操作;在实际撰写的employee data manager中,开发人员确实需要提供基本的SQL语句,就像在SqlCommond中设置的那样(Borland ECO则更进一步,以OCL代替了SQL);
(3) ObjectBroker的作用非常重要,它是对象与数据间的桥梁,RegisterPersistent方法建立了这种虚拟(Object)与现实(RDBMS)间的关系;
(4) 在employee business object的声明中,对象属性与数据库字段的对应关系是通过.NET Attribute机制体现的,所以修改起来还是比较方便的,虽然相比配置文件的方式显得不够灵活(请参考OJB的介绍),比如:需要重新编译,开发人员不得不关注数据库字段等。
OJB:
从图中不难看出: (1) 该方案的实现比较复杂,但用户需要实际撰写的代码变少了(只需要编写employee business object),这其中的关键就在于引入了配置文件;同时,由于配置文件的引入,我们在hello world application中也不需要调用类似OPF解决方案(请参考上文的OPF类图)中的RegisterObject方法,所有这一切(甚至包括数据库连接信息),系统都已了如指掌!
(2) 该方案中,SQL命令通过Criteria类被彻底替代,而QueryFacade则充当了Adapter的功能,通过PersistenceBroker这一真正的Command与数据库进行通信;
(3) 无论是repository.xml配置文件,还是Criteria、QueryFacade类,我们都可以在ObjectSpaces(请参考下面的介绍)中找到类似的实现(难道是巧合?),同时,作者个人以为,这种方式也更符合O/R Mapping的精神,减轻了开发人员的负担!
(4) OJB还有一个非常cool的工具“repositorygen.exe”,可以用来生成repository.xml配置文件(同样的,源码无偿奉上J),这一点,甚至连ObjectSpaces都没能做到(想想那么多字段、属性、关联、映射,简直可以让人发疯J)!
? Microsoft ObjectSpaces 这是一个在几年前就让众多.NET guy伸长脖子激动不已的技术。就作者来说,那个时候,只要一提起这个话题,一般都是在J2EE guy的嘲笑声中悻悻而归,恨不能自己也搞个ENB(相对EJB)或者NCMP(相对CMP)什么的。 终于,我们可以在.NET Framework 1.2(可在VS.NET 2004Whidbey或Yukon中找到,目前都是Beta版本)中一睹其“芳容”了J。
首先,让我们看看用ObjectSpaces写出的代码是什么样子(依然使用上面的employee例子):
// 初始化ObjectSpace SqlConnection conn = new SqlConnection("Data Source=localhost; Integrated Security=SSPI; Database=Northwind"); ObjectSpace os = new ObjectSpace("map.xml", conn);
// 根据EmployeeID返回其Title Employee oEmp = (Employee)os.GetObject ( new ObjectQuery(typeof(Employee), "ID = 1")); // 注意:实际字段名为:EmployeeID string strTitle = oEmp.Title; …… // 根据 City 返回所有符合条件的 Employee ObjectSet oSet = os.GetObjectSet( new ObjectQuery(typeof(Employee), "City = '”Seattle'")); // 注意:返回的不是DataTable,而是对象集合 foreach (Employee oEmp in oSet) { …… // 注意:在这里可以对oEmp做任何操作 }
针对上面第二段代码,还有一种解决方案,就是以ObjectReader替代ObjectSet,这其中所包含的差异,类似于ADO.NET 1.0(包含ObjectSpacesd的ADO.NET又称为ADO.NET 2.0)中的DataSet / DataTable与DataReader间的不同(不得不佩服Microsoft在前后一致性上表现出的老谋深算J)。
仔细分析上面的代码,就可以发现它和前面讨论的OJB有惊人的相似点(OJB中作者只画了基本类图,但足可看出这种思想上的接近)! 例如:ObjectSpace类基本上提供了OJB中的QueryFacade功能;ObjectQuery类基本上提供了OJB中的Criteria功能;同时,两种解决方案又不约而同的使用了配置文件来存储O/R Mapping信息;而应用程序一般也就通过这2个类进行数据操作,非常方便。稍微有些区别的可能是在数据返回格式上(这一点,ObjectSpaces考虑得更细致,可以参考上面的代码),但这已经对实际的代码实现影响不大了。 如果将ObjectSpaces下的调用代码与前面给出的那段在ADO.NET下撰写的代码作个比较,不难看出,ObjectSpaces给出的代码更易阅读和理解,就算不熟悉ADO.NET整体架构的开发人员,也可轻松上手(唯一涉及RDBMS的代码只有建立数据库连接时需要)。对于已经熟悉ADO.NET或曾接触过O/R Mapping(如:J2EE下的Hibernate)的朋友来说,真可谓小菜一碟!
从.NET Framework 1.2文档中可以知道,ObjectSpaces总共提供了3个命名空间,整体结构非常清晰: System.Data.ObjectSpaces System.Data.ObjectSpaces.Query System.Data.ObjectSpaces.Schema
ObjectSpaces、Query已在上面的代码中见识过,从名字中可以猜出,它们主要负责向外提供基本访问接口(如查询、增 / 删 / 改等)和解析各种查询条件(如对象过滤等),Schema命名空间则主要用来操作O/R Mapping配置文件,并为其它两个命名空间中的类提供服务。 在ObjectSpaces中,O/R Mapping配置文件主要指map.xml,这个文件的名字是可以随意更换的,比较类似OJB中的repository.xml。另外两个分别描述数据库结构和对象结构的配置文件也非常重要:RSD.xml(Relational Schema Definition),OSD.xml(Object Schema Definition)。可以将它们理解为Typed DataSet中的XSD文件,没有它们,所有的数据 / 对象Mapping和Validation都将是“非法”的J! 本文中,作者不准备对ObjectSpaces来个深度探索,也不会提供什么Sample说明其优越性,这方面,.NET Framework SDK早已为大家提供了丰富套餐。 作者只是希望,如果从DAL的角度来分析,ObjectSpaces技术能为我们带来什么,是否意味着从此告别DataReader / DataSet,抑或为开发人员带来了新的烦恼?
好处不多说,仅举数例即可明了: (1) ObjectSpaces全部采用对象方式访问数据,大大缓解了很多开发人员的SQL(或者说RDBMS)恐惧症;
(2) 对于比较简单的数据库结构变化,只需要修改配置文件即可,无需重新编译代码(较之OPF中将映射关系以.NET Attribute方式封装于代码中,显得更加灵活、方便);
(3) 对于比较复杂的数据库结构变化,由于只涉及对象操作,所以修改的工作也要比以前简单许多;
(4) 采用了O/R Mapping配置文件后,数据库设计与DAL开发可以分别进行,相互的影响也降到了最低点;
不足则是我们更须关注的话题: (1) 目前版本不支持中文(永远的话题J)查询,不爽!
(2) 当前版本仅支持SQL Server 2000以上版本的数据库系统,弱(这是个很耐人寻味的限制,有兴趣的读者不妨想想到底是什么原因)! (1、2引自.NET Framework SDK Document,就这两点已排除了很多跃跃欲试的朋友。而作者参与的.NET项目虽不受1影响,但由于经常使用Oracle,就不得不暂时忍痛割爱了J)
(3) 性能问题。虽然ObjectSpaces也提供了类似DataReader的功能(ObjectReader),但毕竟需要进行一次数据强类型填充,无论如何会有损失,如果返回数据量变大,将是一个不得不考虑的问题;
(4) 还是性能问题。map.xml是个好东东,但如何优化对它的访问以及进行正确的Validation(基于RSD.xml、OSD.xml)毕竟需要时间,甚至在某些时候(数据库结构比较复杂),这会造成比第3点更为严重的后果;
说了些不足,其实也无须过于担心,毕竟,没有十全十美的解决方案,怎么取舍就看你自己的决定了。 本章最后,作者给出了一个自己的总结,可供您参考一二。在所有的分析完毕之后,作者也试图结合自己的实践提供“我的方案(撰写中)”,希望能给各位读者带来帮助。 ? Borland ECO 素以提供“多快好省”组件著称的Borland公司在微软发布ObjectSpaces之前率先推出了一套新的开发框架:ECO(Enterprise Core Object),先不说其技术特点,就凭其与建模工具Together的无缝集成,不得不让人佩服Borland在统一开发过程方面所下的功夫。
根据Borland在ECO介绍中的定义,简单说,ECO就是:模型驱动(MDA-Driven)的.Net数据库应用(Database Application)开发框架(Framework)。 (观点:以作者看来,数据库应用的核心问题就是DAL,也就是本文需要讨论的话题)
很明显,从MDA-Driven,Database Application和Frmework这些很具震撼效果的词语不难看出,它们正是Borland公司以前及现在都无比强大的核心竞争力所在(当然,还包括IDE、Components、Cross Platform等方面,一个公司能有这么多优秀的产品,实在令人尊敬)! 以前,在Borland平台上开发数据库应用已经非常方便,组件+可视化设计基本可以拿下比较简单的应用模块。如今则更上一层,终于推出了自己的O/R Mapping解决方案! 以作者的观点,在Together这样优秀的Modeling工具加盟下,配合在Framework开发方面的数一数二实力(仅以艺术性而论,VCL Framework就可以拿该领域的奥斯卡奖),目前的ECO绝对稳坐.NET O/R Mapping第一把交椅! (注意:ObjectSpaces尚未发布,暂不考虑。)
关于ECO具体开发方面的介绍,大家可以参考“程序员,2003.12,P99”,“程序员,2004.01,P97”,“程序员,2004.02,P100”。
以下,作者主要分析利用ECO开发所带来的种种好处及其不足,供各位参考。
优点: (1) 与Typed DataSet(类型化的DataSet,在VS.NET中可自动生成)相比具有明显优势;除了可以在UML/ 代码间自由切换外,ECO可以支持自定义数据类型 和计算类型(用过Delphi的朋友都知道这是个异常 强大的武器);
(2) IDE提供强大的设计时支持,各种工具、组件一应俱全,这也是Borland最擅长的领域;
(3) 集成Together建模工具,将MDA发挥得淋漓尽致;之所以将这条放在第3位,请参考下面的缺点分析;
(4) 引入Object Constraints Language(OCL),该标准得到了OMG组织的官方支持,号称:OO SQL(面向对象的SQL),对于不熟悉SQL的开发人员是一大福音;
缺点: (1) 资源消耗较大,普通机器难以体会其优势;这方面,Rational XDE倒是做得相当不错;
(2) 有一定学习曲线,比如:OCL(Object Constraints Language),虽然与SQL不同,但从语法角度还不算十分简洁;就作者自己的体会,可能学习XQuery(XML中的查询语言)或OPath(ObjectSpaces中使用的查询语言)要更轻松一些;
(3) 纯商业产品,只有Architect版本才包含此功能,而ObjectSpaces直接包含在.NET Framework中,与VS.NET版本无关,可以通过.NET Framework SDK直接使用;
(4) 轻微过度MDA:DAL开发人员既然可以在ECO中生成数据库脚本,那DBA是否也需要在ECO中进行设计呢? 对于企业级应用,作者个人以为:MDA开发模式比较适用于DAL以上各层的开发,如:System Architecture,Business Fa?ade,Business Logic,甚至User Interface,而对于Data Storage,可能并不是非常需要MDA介入。 试想一下,O/R Mapping提供了映射关系,就是希望将RDBMS与其它层分离,如果现在全部在一个地方搞定,那岂不是又加重了开发人员的负担(有时候自动化不见得越多越好)? 还有一点:ECO宣称“代码/UML双向同步”,但并没有保证“代码/字段可以不同”,这就在一定程度上丧失了灵活性! (提示:UML中“同步”意味着设计方案与代码框架“一致”,但在O/R Mapping中,反而不要求这种“一致”,只需要在DAL与Schema间建立对应关系既可,而Borland将传统建模的思想照搬到DAL设计中,作者认为是值得商榷的。这方面,其它的O/R Mapping方案就做得比较自然,突出了“Mapping”的含义,而不仅仅是简单的“Synchronization”。)
? 其它 还有一些其它的技术也可以实现O/R Mapping或类似功能,就作者试用过的一些解决方案来说,Constructor(国外)、Grove(国内)都是不错的选择,Constructor甚至号称“Model Driven RAD for .NET”,有兴趣的朋友可以访问如下站点:
说了许多,又到了“综上所述”时间,作者再次放胆建议: (1)与基于ADO.NET的DAL实现方式不同,O/R Mapping有巨大 优势,但也同时隐藏着风险:
n 最严重的问题就是与存储过程的冲突! 众所周知,O/R Mapping的本质是映射,在这方面各类实现 都有其严格定义,说白了,就是将表操作转化为对象操作。 但存储过程的灵活性(传入参数,返回结果等)却不得不使 其暂时地被排除在O/R Mapping大家庭外(至少在目前,上 述介绍过的OJB、ObjectSpaces等方案都不支持存储过程); 另一方面,如果我们希望实现比较复杂的数据逻辑时,却不 得不以新的Object Language(如:OCL、OQL等)书写原 本可以封装在存储过程中的复杂SQL语句。而且,即使写 出了这些数据逻辑,也会令人感觉非常古怪甚至丑陋(某种 意义上,这也违背了O/R Mapping的初衷)!
n 第二个问题是:在O/R Mapping的环境中,系统的执行效率将有一定损失,即使使用了Cache Management(一次性装载配置文件)或者Delayed Loading(又称Lazy Loading,只在访问实体类数据时才真正连接数据库)技术,由于在初次装载数据时使用了Reflection机制去定位实体类(在某些方案中,映射关系以.NET Attribute方式体现,则更花时间),所以肯定不如直接通过ADO.NET填充数据来得那么快!
如果各位认为上述风险不是问题,那下面的各条就是作者的真正建议了。
(2)在大型应用(一般指企业级应用)开发中,ECO是很好的选择; (3)如果是普通n-Tier应用,则ObjectSpaces足矣; (4)想要学习O/R Mapping的朋友,可以看看OPF / OJB源码,这两个方案的实现思路与ECO / ObjectSpaces是有一些相似的; (5)如果不希望使用现成的O/R Mapping,则可在OPF / OJB的基础上按需裁减,或者参考下面的“设计自己的持久层”中提出的方案。 u 其它 ? ASTA 经常在Delphi下进行网络数据库应用开发或者曾经使用过Borland Midas技术的朋友,对ASTA应该不会陌生。 如果说基于ADO.NET或O/R Mapping来实现DAL是少林、武当的正宗心法,那ASTA就有点明教“乾坤大挪移”的味道:将整个DAL中的Data Access几乎完全扔到了另一个地方(和打回原处稍有区别,但也算另一种挪移J)进行处理!
传统的DAL实现大都是这样的:
ASTA则另辟蹊径,额外加入了一个“中间层”:
一般来说,中间层的引入是为了提高灵活性,减少耦合度, ASTA就是利用了这个特性设计出来的。 上图的一个关键点就在于ASTA Client与ASTA Server之间 的那个“网络连接”,ASTA技术正是利用了网络特性,间接地 屏蔽了不同数据库系统间的差别,使开发人员在设计DAL时完 全不用知道Database的存在!这种情况有点像浏览器,完全不 用知道HTML页面是来自于IIS还是Apache,只需要知道自己 是利用HTTP协议通过网络来获得HTML页面!
在ASTA中,浏览器就是ASTA Client,HTML页面就是ASTA DataSet(与ADO.NET中的DataSet在格式上有区别,此处可以认为是另一种数据库无关的数据集合表示方式),而IIS、Apache就是ASTA提供的不同的ASTA Server(目前支持大部分主流数据库系统,开发人员也可以撰写自己的ASTA Server),HTTP协议自然就是ASTA Client与ASTA Server间的通信协议! 从技术上分析,ASTA架构的目的之一就是利用自己提供的协议来屏蔽不同数据库系统的网络通信协议,从而使客户端(DAL)完全摆脱在编写网络数据库应用时的通信难题! (作者还记得,.NET出来前,每次在编写面向Internet 的SQL Server应用程序时,除了需要配置连接字符串,还要在客户端特别安装SQL Server Client Network Utility以配置Internet连接,不说SQL Server暴露端口引起的安全问题,就是这么一番折腾也让人够呛的J)
在ASTA下编写程序非常舒服,只要知道ASTA Server的地址和端口,再提供一定权限的用户名、口令(ASTA自带的认证系统,开发人员也可以撰写自己的Authentication/Authorization模块,甚至直接利用数据库系统提供的验证机制),立刻就能得到任何数据库的任何数据信息!而且,一旦数据库系统有所变动(如从SQL Server切换到Oracle,但不包括数据库结构的改动),客户端根本无须任何修改! ASTA技术不足处也是很明显的:由于引入了在某些情况下 (如一次返回数据量很大时)足以致命的“网络中间层”,使开 发人员在开发大型应用(尤其是面向Internet的企业级应用)时 需要特别小心,毕竟,舒服在某些时候是需要付出一定代价的!
有兴趣的朋友不妨去这个网站看看:
? .NET Remoting / WebServices 原来不准备将.NET Remoting / WebServies拿出来凑热闹,毕竟,这两种技术不是为DAL度身定做的。 无奈,偏偏就是看到有朋友这么用过,后来细想,发现也颇有一些道理,就拿出来与大家一起参详参详。 其实,虽然微软大肆宣传XML WebServices,但就技术来看,其实只要论述.NET Remoting一种就可以了,XML WebServices无非就是在.NET Remoting中使用了HTTP + SOAP通信协议的一种特例而已。
.NET Remoting应用到DAL中可能出于两种目的: (1) 希望实现跨平台数据访问,因为ADO.NET中的DataSet是可以被序列化的,通过Remoting或WebServices可以在客户端恢复现场;
(2) 减轻服务器压力,增加系统灵活性。 这有点类似于上面的ASTA技术,只不过这里的协议 换成了.NET Remoting Protocal。但即使用这种方式, 还是和ASTA的灵活性有区别,毕竟,Remoting要 求在客户端注册每一个DAL的访问接口,一旦接口 变化,接口必须重新注册!
所以,作者的建议是:尽量避免使用.NET Remoting来 构建应用程序的DAL模块(如果是Business Logic Layer,则不 存在这个问题),除非真的是“迫不得已”(例如:必须在DAL 层实现分布式计算或者客户坚持使用.NET Remoting J)!
|
.net数据层
最新推荐文章于 2023-02-27 10:23:31 发布