《解剖PetShop》体系之二

二、PetShop数据访问层之数据库访问安插
在浩如烟海一中,我从总体上分析了PetShop的架构设计,并提及了分支的定义。从本有的开端,我将顺序对各层进行代码级的分析,以求获得进一步细心而深切的敞亮。在PetShop
4.0中,由于引入了ASP.Net
2.0的有的新特性,所以数据层的始末也越加的广阔和复杂性,包涵:数据库访问、Messaging、MemberShip、Profile四部分。在一连串二中,我将介绍有关数据库访问的规划。

在PetShop中,系统要求处理的数据库对象分为两类:一是数额实体,对应数据库中相应的数据表。它们从不作为,仅用于表现对象的数额。那么些实体类都被放到Model程序集中,例如数据表Order对应的实体类OrderInfo,其类图如下: 

图片 1

那几个目的并不有所持久化的功用,不难地说,它们是作为数据的载体,便于工作逻辑针对相应数据表举行读/写操作。即便这一个类的质量分别映射了数据表的列,而每一个对象实例也恰好对应于数据表的每一行,但那几个实体类却并不富有相应的数据库访问能力。

由于数量访问层和业务逻辑层都将对那个数据实体进行操作,因而先后集Model会被那两层的模块所引述。

其次类数据库对象则是数码的事务逻辑对象。那里所指的事务逻辑,并非业务逻辑层意义上的圈子(domain)业务逻辑(从这么些意思上,我更倾向于将事情逻辑层称为“领域逻辑层”),一般意义上说,这个工作逻辑即为基本的数据库操作,包罗Select,Insert,Update和Delete。由于这一个事情逻辑对象,仅具有行为而与数量无关,因而它们均被架空为一个独门的接口模块IDAL,例如数据表Order对应的接口IOrder: 

图片 2

将数据实体与相关的数据库操作分离出来,符合面向对象的精神。首先,它反映了“职责分开”的口径。将数据实体与其表现分别,使得两者之间信赖裁减,当数码表现发生变更时,并不影响Model模块中的数据实体对象,防止了因一个类义务过多、过大,从而致使该类的引用者发生“磨难性”的熏陶。其次,它反映了“抽象”的动感,或者说是“面向接口编程”的特等呈现。抽象的接口模块IDAL,与实际的数据库访问完毕完全隔绝。那种与完成毫不相关的计划性,有限支撑了系统的可增加性,同时也准保了数据库的可移植性。在PetShop中,可以支撑SQL
Server和Oracle,那么它们具体的完成就分别位于五个不等的模块SQLServerDAL、OracleDAL中。

以Order为例,在SQLServerDAL、OracleDAL四个模块中,有两样的落到实处,但它们同时又都完成了IOrder接口,如图: 

图片 3

从数据库的贯彻来看,PetShop浮现出了没有ORM框架的重合与丑陋。由于要对数据表举行Insert和Select操作,以SQL
Server为例,就采纳了SqlCommand,SqlParameter,SqlDataReader等对象,以形成这一个操作。越发复杂的是Parameter的传递,在PetShop中,使用了多量的字符串常量来保存参数的称谓。别的,PetShop还专程为SQL
Server和Oracle提供了用空想来欺骗别人的Helper类,包装了部分常用的操作,如ExecuteNonQuery、ExecuteReader等办法。

在一向不ORM的场所下,使用Helper类是一个相比好的政策,利用它来已毕数据库基本操作的包装,可以削减过多和数据库操作有关的代码,那展示了对象复用的口径。PetShop将这些Helper类统一置于DBUtility模块中,分裂数据库的Helper类暴光的主意基本相同,只除了有些特有的渴求,例如Oracle中处理bool类型的章程就和SQL
Server差异,从而专门提供了OraBit和OraBool方法。此外,Helper类中的方法均为static方法,以利于调用。OracleHelper的类图如下: 

图片 4

对于数据访问层来说,最胃痛的是SQL语句的处理。在早期的CS结构中,由于未使用三层式架构设计,数据访问层和工作逻辑层是严密糅合在联合的,因而,SQL语句遍布与系统的每一个角落。那给程序的保证带来巨大的辛勤。其余,由于Oracle使用的是PL-SQL,而SQL
Server和Sybase等使用的是T-SQL,两者尽管都坚守了正规SQL的语法,但在不少细节上仍有分别,如若将SQL语句多量的选拔到程序中,无疑为可能的数据库移植也拉动了不便。

最好的法门是拔取储存进程。那种方法使得程序更为清洁,其余,由于存储进度可以以数据库脚本的花样存在,也便于移植和改动。但那种艺术依然有缺点。一是储存进度的测试相对劳顿。即便有照应的调试工具,但比起对代码的调剂而言,照旧相比复杂且不便宜。二是对系统的立异带来阻力。假设数据库访问是由程序完毕,在.Net平台下,大家仅须求在修改程序后,将另行编译的次序集xcopy到布署的服务器上即可。借使利用了蕴藏进度,出于安全的考虑,必须有特意的DBA重新运行存储进程的剧本,陈设的艺术备受了限定。

自家早已在一个品种中,利用一个专门的表来存放SQL语句。如要使用有关的SQL语句,就动用重大字搜索得到对应语句。那种做法近似于存储进度的调用,但却防止了安插上的题材。但是这种措施却在性质上不可以赢得有限协助。它仅符合于SQL语句较少的光景。不过,利用得天独厚的规划,大家得以为各样业务提供不相同的表来存放SQL语句。同样的道理,这几个SQL语句也足以存放到XML文件中,更有利于系统的恢弘或修改。可是前提是,我们需求为它提供专门的SQL语句管理工具。

SQL语句的采用不能避免,怎样更好的使用SQL语句也无定论,但有一个标准化值得我们遵循,就是“应该尽可能让SQL语句尽存在于数量访问层的现实贯彻中”。

理所当然,若是拔取ORM,那么万事就变得不一样了。因为ORM框架已经为数据访问提供了着力的Select,Insert,Update和Delete操作了。例如在NHibernate中,我们得以向来调用ISession对象的Save方法,来Insert(或者说是Create)一个数码实体对象:
public void Insert(OrderInfo order)
{
    ISession s = Sessions.GetSession();
    ITransaction trans = null;
    try
    {
    trans = s.BeginTransaction();
      s.Save( order);
      trans.Commit();
    }
    finally
    {
      s.Close();
    }
}

一贯不SQL语句,也未尝那么些烦人的Parameters,甚至不须要特地去考虑工作。其余,那样的布置性,也是与数据库无关的,NHibernate可以经过Dialect(方言)的编制支持分裂的数据库。唯一要做的是,我们须求为OrderInfo定义hbm文件。

当然,ORM框架并非是万能的,面对纷纭复杂的事情逻辑,它并不可以一心消灭SQL语句,以及代表复杂的数据库访问逻辑,但它却很好的反映了“80/20(或90/10)法则”(也被喻为“帕累托法则”),也就是说:花比较少(10%-20%)的劲头就可以化解一大半(80%-90%)的难题,而要解决剩余的少部分题材则需要多得多的竭力。至少,那个在数额访问层中占据了多边的CRUD操作,通过利用ORM框架,大家就仅需求提交极个别年华和生机来化解它们了。那如实减少了总体项目支出的周期。

照旧回到对PetShop的座谈上来。现在我们早就有了多少实体,数据对象的架空接口和落到实处,可以说关于数据库访问的重点就早已成功了。留待大家的还有两个难点必要缓解:
1、数据对象创造的管理
2、利于数据库的移植

在PetShop中,要创制的数码对象包蕴Order,Product,Category,Inventory,Item。在头里的设计中,这几个目标已经被架空为对应的接口,而其完成则依据数据库的两样而有所分化。也就是说,创制的目的有多样种类,而每连串型又有不相同的兑现,那是压倒元白的肤浅工厂格局的利用场景。而地点所述的多个难题,也都能够通过架空工厂方式来化解。标准的无济于事工厂形式类图如下: 

图片 5

譬如说,创造SQL Server的Order对象如下:
PetShopFactory factory = new SQLServerFactory();
IOrder = factory.CreateOrder();

要考虑到数据库的可移植性,则factory必须作为一个全局变量,并在主程序运行时被实例化。但那样的设计纵然曾经达成了“封装变化”的目的,但在开创PetShopFactory对象时,仍不可幸免的出现了切实的类SQLServerFactory,也即是说,程序在这么些局面上暴发了与SQLServerFactory的强看重。一旦整个连串须求辅助Oracle,那么还须要修改那行代码为:
PetShopFactory factory = new OracleFactory();

修改代码的这种表现显明是不足接受的。解决的形式是“依赖注入”。“器重注入”的效应日常是用特其他IoC容器提供的,在Java平台下,那样的容器包涵Spring,PicoContainer等。而在.Net平台下,最普遍的则是Spring.Net。可是,在PetShop系统中,并不须要专门的容器来贯彻“着重注入”,简单的做法照旧使用配置文件和反光作用来促成。也就是说,大家可以在web.config文件中,配置好实际的Factory对象的完好的类名。但是,当大家选择配置文件和反光成效时,具体工厂的创导就浮现有些“画蛇添足”了,大家完全可以在布置文件中,直接指向具体的数据库对象完毕类,例如PetShop.SQLServerDAL.IOrder。那么,抽象工厂形式中的相关工厂就足以简化为一个厂子类了,所以自己将那种方式称之为“具有简易工厂特质的肤浅工厂形式”,其类图如下: 

图片 6

DataAccess类完全代替了前头创制的工厂类种类,它是一个sealed类,其中创设各类数据对象的点子,均为静态方法。之所以能用那么些类达到抽象工厂的目的,是因为安插文件和反光的行使,如下的代码片断所示:
public sealed class DataAccess
{
 // Look up the DAL implementation we should be using
    private static readonly string path =
ConfigurationManager.AppSettings[“WebDAL”];
    private static readonly string orderPath =
ConfigurationManager.AppSettings[“OrdersDAL”];

 public static PetShop.IDAL.IOrder CreateOrder()
 {
         string className = orderPath + “.Order”;
         return
(PetShop.IDAL.IOrder)Assembly.Load(orderPath).CreateInstance(className);
    }
}

在PetShop中,那种借助配置文件和反光制造对象的方法最好广泛,包括IBLLStategy、CacheDependencyFactory等等。那一个完成逻辑散布于全部PetShop系统中,在我看来,是可以在此基础上展开重构的。也就是说,我们得以为全方位种类提供类似于“ServiceLocator”的完成:
public static class ServiceLocator
{
 private static readonly string dalPath =
ConfigurationManager.AppSettings[“WebDAL”];
    private static readonly string orderPath =
ConfigurationManager.AppSettings[“OrdersDAL”];
 //……
 private static readonly string orderStategyPath =
ConfigurationManager.AppSettings[“OrderStrategyAssembly”];

 public static object LocateDALObject(string className)
 {
  string fullPath = dalPath + “.” + className;
  return Assembly.Load(dalPath).CreateInstance(fullPath);
 }
public static object LocateDALOrderObject(string className)
 {
  string fullPath = orderPath + “.” + className;
  return Assembly.Load(orderPath).CreateInstance(fullPath);
 }
public static object LocateOrderStrategyObject(string className)
 {
  string fullPath = orderStategyPath + “.” + className;
  return Assembly.Load(orderStategyPath).CreateInstance(fullPath);
 }
 //……
}

那就是说和所谓“依赖注入”相关的代码都可以行使ServiceLocator来成功。例如类DataAccess就足以简化为:
public sealed class DataAccess
{
 public static PetShop.IDAL.IOrder CreateOrder()
 {
         return (PetShop.IDAL.IOrder)ServiceLocator.
LocateDALOrderObject(“Order”);
    }
}

经过ServiceLocator,将具有与布局文件有关的namespace值统一保管起来,那便于种种动态创立对象的保管和将来的掩护。

相关文章

网站地图xml地图