前言
在三层结构中,数据库是最底层,负责对数据库的操作,如添加,删除,修改数据等.上一篇中最后一幅图"各项目相互之间的关系和调用图"也清晰的反映出了这一点.从图中我们还可以发现,DAL中大量的采用了"Factory"的形式.到底什么是"Factory"呢?,"Factory"在DAL中起到什么样的作用呢?带着这些疑问我们开始吧.
工厂模式
设计模式是一门专门的学科,也是需要在实践中不断积累的经验,目前流行的的设计模式很多,其中最简单的可能要属"工厂模式"了.
工厂模式最主要的特点就是工厂类(A)根据调用者传递过来的参数(P)形式,通过智能分析判断,选择返回一组拥有共同父类(F)的子类(F1,F2...)中的一个的实例,其中每个子类针对父类完成不同的操作,即工厂类根据条件创建不同的子类,以完成不同的工作.工厂模式的原型如下图所示:
PetShop 4系统中的工厂模式
Petshop 4中大量运用了工厂设计模式,项目名称中以"Factory"结尾的都是抽象的工厂类,如下:
- DALFactory:数据访问层的抽象工厂(决定创建哪种数据库类型的数据访问层。可以选择:SQLServer,Oracle)
- CacheDependencyFactory:缓存依赖类的工厂类。(创建具体表的缓存依赖)
- MessagingFactory :异时处理消息队列的抽象工厂(反射创建具体的异时处理类)
- ProfileDALFactory:ProfileDAL的工厂类(反射选择创建Oracle 和SQL Server的 ProfileDAL)
以DALFactory为例,当用户访问数据库时,用户调用DALFactory类,DALFactory类根据用户使用的数据库的类型,决定是实例化SQLServerDAL还是OracleDAL,而这两个DAL类都继承自IDAL类,当实例类完成操作后,返回IDAL类.这样,对于用户来说,不管使用的是什么类型的数据库,调用方法和返回结果都是一样的.这样做的好处就非常明显了:当由SQLServer数据库切换来Oracle数据库上时,不需要修改任何程序.
数据层的数据访问
前面简单了解了一下PetShop4系统中的工厂模式,在DALFactory类中,有一个重要的父类:IDAL类,它是实现数据访问的接口集合.首先来了解一下接口的定义:接口只是一种约束形式,它中包括成员的定义,而不定义成员的具体实现.也就是说,接口只是一个具有成员框架的空壳子.它必须被继承,并在继承类中实现成员的内容.接口的主要目的是为不相关的类提供一个通用的处理服务.它可以实现多继承.
在IDAL的接口中,定义了全部数据库访问所需要的方法.但是是在SQLServerDAL和OracleDAL两个类中去实现的.这样做的好处是有利于实现数据封装的安全性和有利于将来的扩展.
IDAL类中的5个数据访问接口
接口名 | 所在的.cs文件名 | 说明 |
ICategory | ICategory.cs | 定义对商品类别的访问方法 |
IInventory | IInventory.cs | 定义对商品数量的访问方法 |
IItem | IItem.cs | 定义对商品详细信息的访问方法 |
IOrder | IOrder.cs | 定义对订单的访问方法 |
IProduct | IProduct.cs | 定义对商品的访问方法 |
以IProduct接口为例:
/// <summary>
/// Interface for the Product DAL
/// </summary>
public interface IProduct{
/// <summary>
/// Method to search products by category name
/// </summary>
/// <param name="category">Name of the category to search by</param>
/// <returns>Interface to Model Collection Generic of search results</returns>
IList<ProductInfo> GetProductsByCategory(string category);
/// <summary>
/// Method to search products by a set of keyword
/// </summary>
/// <param name="keywords">An array of keywords to search by</param>
/// <returns>Interface to Model Collection Generic of search results</returns>
IList<ProductInfo> GetProductsBySearch(string[] keywords);
/// <summary>
/// Query for a product
/// </summary>
/// <param name="productId">Product Id</param>
/// <returns>Interface to Model ProductInfo for requested product</returns>
ProductInfo GetProduct(string productId);
}
注:
- 大家在查看这些接口定义的时候,会发出文件中仍然保留了命名空间的形式,其实,在ASP.NET3.5中,已经取消了显示的命名空间.
- 接口定义的代码中还使用了泛形.关于泛开的知识,我自己还有待加强.
SQLServerDAL的实现
SQLServerDAL中的Product类将SQL语句和其参数定义成了多个常量,这样更于维护,仔细分析这些常量,发现它们的组合非常灵活,可以方便的实现多种条件下的查询,比如按类别,按ID号,还可以按名称和类别ID来模糊查询等.
//Static constants
private const string SQL_SELECT_PRODUCTS_BY_CATEGORY = "SELECT Product.ProductId, Product.Name, Product.Descn, Product.Image, Product.CategoryId FROM Product WHERE Product.CategoryId = @Category";
private const string SQL_SELECT_PRODUCTS_BY_SEARCH1 = "SELECT ProductId, Name, Descn, Product.Image, Product.CategoryId FROM Product WHERE ((";
private const string SQL_SELECT_PRODUCTS_BY_SEARCH2 = "LOWER(Name) LIKE '%' + {0} + '%' OR LOWER(CategoryId) LIKE '%' + {0} + '%'";
private const string SQL_SELECT_PRODUCTS_BY_SEARCH3 = ") OR (";
private const string SQL_SELECT_PRODUCTS_BY_SEARCH4 = "))";
private const string SQL_SELECT_PRODUCT = "SELECT Product.ProductId, Product.Name, Product.Descn, Product.Image, Product.CategoryId FROM Product WHERE Product.ProductId = @ProductId";
private const string PARM_CATEGORY = "@Category";
private const string PARM_KEYWORD = "@Keyword";
private const string PARM_PRODUCTID = "@ProductId";
Product类通过几个方法来实现功能:
- public IList<ProductInfo> GetProductsByCategory(string category)// Query for products by category
- public IList<ProductInfo> GetProductsBySearch(string[] keywords)// Query for products by keywords. The results will include any product where the keyword appears in the category name or product name
- public ProductInfo GetProduct(string productId)// Query for a product
在方法实现的过程中,用到了MS的一个数据库访问助手:SqlHelper类(或OracleHelper类),关于SqlHelper类的介绍,在我以前的博文中就过说明,有兴趣的朋友的可以找到了解一下.
OracleDAL的实现和SQLServerDAL相似,这里不再多加阐述.
DALFactory的实现
DALFactory中,一个重要的方面就是判断使用的数据库类型,这个工作是由DaraAccess类来完成的.该类中一个重要的静态变量path如下:
// Look up the DAL implementation we should be using
private static readonly string path = ConfigurationManager.AppSettings["WebDAL"];
ConfigurationManager是专门来用获取Web.congig文件中节点信息的类,path来自于AppSettings结点的WebDAL项目.打开Web.config文件,可以找到:
<add key="WebDAL" value="PetShop.SQLServerDAL"/>
这个WebDAL项目指明了当前使用的数据库的类型.修改它就可以修改下面要实例化的DAL类了.
DALFactory判断了要实例化的类,那又是怎么实施实例化的呢?
在DaraAccess类中,有5个与IDAL类中名称相似的类.它们就是用来实例化IDAL中的接口的方法.以CreateCategory方法为例:
public static PetShop.IDAL.ICategory CreateCategory() {
string className = path + ".Category";
return (PetShop.IDAL.ICategory)Assembly.Load(path).CreateInstance(className);
}
上面的返回值很值得研究:
- 首先,采用了强制类型转换,将返回结果转换成ICagegory类型.
- 其次,使用Load来加载指定的数据库处理类.
- 最后,使用CreateInstance方法,从程序集中查找指定的类型,然后创建它的实例.
※※※※※※※※※※※※※※--我的程序人生--※※※※※※※※※※※※※※