??xml version="1.0" encoding="utf-8" standalone="yes"?> DBUtility 数据库访问组件基c?/p>
二、项目引用关p?br />
1、Web 引用 BLL?br />
2、BLL 引用 IDALQModelQ用DALFactory创徏实例?br />
3、IDAL 引用 Model?br />
4、Model 无引用?br />
5、DALFactory 引用IDALQ通过dweb.config里设|的E序集,加蝲cȝ实例Q返回给BLL使用?br />
6、SQLServerDAL 引用 Model和IDALQ被DALFactory加蝲的程序集Q实现接口里的方法? 注意Q?br />
1、web.config里的E序集名U必MSQLServerDAL里的输出E序集名UC致?br />
2、DALFactory里只需要一个DataAccessc,可以完成创徏所有的E序集实例?br />
3、项目创建后Q注意修改各目的默认命名空间和E序集名U?br />
4、注意修改解x案里的项目依赖?br />
5、注意在解决Ҏ里增加各目引用?/p>
4.3.1 CacheDependency接口 PetShop 4.0引入了SqlCacheDependencyҎ,对Category、Product和Item数据表对应的~存实施了SQL Cache Invalidation技术。当对应的数据表数据发生更改后,该技术能够将相关从~存中移除。实现这一技术的核心是SqlCacheDependencyc,它承了CacheDependencycR然而ؓ了保证整个架构的可扩展性,我们也允许设计者徏立自定义的CacheDependencyc,用以扩展~存依赖。这有必要为CacheDependency建立抽象接口Qƈ在web.config文g中进行配|?/p>
在PetShop 4.0的命名空间PetShop.ICacheDependency中,定义了名为IPetShopCacheDependency接口Q它仅包含了一个接口方法: AggregateCacheDependency?Net Framework 2.0新增的一个类Q它负责监视依赖对象的集合。当q个集合中的L一个依赖项对象发生改变Ӟ该依赖项对象对应的缓存对象都被自动U除?br />
AggregateCacheDependencycvCl合CacheDependency对象的作用,它可以将多个CacheDependency对象甚至于不同类型的CacheDependency对象与缓存项建立兌。由于PetShop需要ؓCategory、Product和Item数据表徏立依赖项Q因而IPetShopCacheDependency的接口方法GetDependency()其目的就是返回徏立了q些依赖的AggregateCacheDependency对象?/p>
4.3.2 CacheDependency实现 CacheDependency的实现正是ؓCategory、Product和Item数据表徏立了对应的SqlCacheDependencycd的依赖项Q如代码所C: protected AggregateCacheDependency dependency = new AggregateCacheDependency(); foreach (string tableName in tables) 需要徏立依赖项的数据库与数据表都配|在web.config文g中,其设|如下: Ҏ各个数据表间的依赖关p,因而不同的数据表需要徏立的依赖也是不相同的,从配|文件中的value值可以看出。然而不徏立依赖项的多寡,其创建的行ؓ逻辑都是怼的,因而在设计Ӟ抽象了一个共同的cTableDependencyQƈ通过建立带参数的构造函敎ͼ完成对依赖项的徏立。由于接口方法GetDependency()的实CQ返回的对象dependency是在受保护的构造函数创建的Q因此这里的实现方式也可以看作是Template Method模式的灵z运用。例如TableDependency的子cProductQ就是利用父cȝ构造函数徏立了Product、Category数据表的SqlCacheDependency依赖Q?br />
public class Product : TableDependency 如果需要自定义CacheDependencyQ那么创Z赖项的方式又有不同。然而不是创徏SqlCacheDependency对象Q还是自定义的CacheDependency对象Q都是将q些依赖Ҏ加到AggregateCacheDependencycMQ因而我们也可以定义CacheDependency建立专门的类Q只要实现IPetShopCacheDependency接口卛_?/p>
4.3.3 CacheDependency工厂 l承了抽象类TableDependency的Product、Category和Itemcd需要在调用时创建各自的对象。由于它们的父类TableDependency实现了接口IPetShopCacheDependencyQ因而它们也间接实现了IPetShopCacheDependency接口Q这为实现工厂模式提供了前提?/p>
在PetShop 4.0中,依然利用了配|文件和反射技术来实现工厂模式。命名空间PetShop.CacheDependencyFactory中,cDependencyAccess即ؓ创徏IPetShopCacheDependency对象的工厂类Q?br />
public static class DependencyAccess 虽然DependencyAccesscdZ实现了IPetShopCacheDependency接口的类Category、Product、ItemQ然而我们之所以引入IPetShopCacheDependency接口Q其目的在于获得创Z依赖的AggregateCacheDependencycd的对象。我们可以调用对象的接口ҎGetDependency()Q如下所C: Z方便调用者,g我们可以对DependencyAccessc进行改q,原有的CreateCategoryDependency()ҎQ修改ؓ创徏AggregateCacheDependencycd对象的方法?/p>
然而这L做法Cؕ了作为工厂类的DependencyAccess的本w职责,且创建IPetShopCacheDependency接口对象的行Z然有可能被调用者调用,所以保留原有的DependencyAccesscM然是有必要的?/p>
在PetShop 4.0的设计中Q是通过引入Facade模式以方便调用者更加简单地获得AggregateCacheDependencycd对象?/p>
4.3.4 引入Facade模式 利用Facade模式可以一些复杂的逻辑q行包装Q以方便调用者对q些复杂逻辑的调用。就好像提供一个统一的门面一般,内部的子系l封装v来,l一Z个高层次的接口。一个典型的Facade模式C意囑֦下所C: Facade模式的目的ƈ非要引入一个新的功能,而是在现有功能的基础上提供一个更高层ơ的抽象Q得调用者可以直接调用,而不用关心内部的实现方式。以CacheDependency工厂ZQ我们需要ؓ调用者提供获得AggregateCacheDependency对象的简便方法,因而创ZDependencyFacadec: DependencyFacadecd装了获取AggregateCacheDependencycd对象的逻辑Q如此一来,调用者可以调用相x法获得创建相关依赖项的AggregateCacheDependencycd对象Q?br />
AggregateCacheDependency dependency = DependencyFacade.GetCategoryDependency(); 比v直接调用DependencyAccesscȝGetDependency()Ҏ而言Q除了方法更单之外,同时它还对CacheDependencyAssembly配置节进行了判断Q如果其gؓI,则返回null对象?/p>
在PetShop.Web的App_Code文g夹下Q静态类WebUtility的GetCategoryName()和GetProductName()Ҏ调用了DependencyFacadecR例如GetCategoryName()ҎQ?br />
public static string GetCategoryName(string categoryId) string cacheKey = string.Format(CATEGORY_NAME_KEY, categoryId); // 查缓存中是否存在该数据项; GetCategoryName()Ҏ首先会检查缓存中是否已经存在CategoryName数据,如果已经存在Q就通过~存直接获取数据Q否则将通过业务逻辑层调用数据访问层讉K数据库获得CategoryNameQ在获得了CategoryName后,会将新获取的数据q同DependencyFacadecd建的AggregateCacheDependency对象d到缓存中?/p>
WebUtility静态类被表C层的许多页面所调用Q例如Product面Q?br />
public partial class Products : System.Web.UI.Page 昄面title的逻辑是放在Page_Load事gҎ中,因而每ơ打开该页面都要执行获取CategoryName的方法。如果没有采用缓存机Ӟ当Category数据较多Ӟ面的显C就会非常缓慢?/p>
4.3.5 引入Proxy模式 业务逻辑层BLL中与Product、Category、Item有关的业务方法,其实现逻辑是调用数据访问层QDALQ对象访问数据库Q以获取相关数据。ؓ了改善系l性能Q我们就需要ؓq些实现Ҏ增加~存机制的逻辑。当我们操作增加了缓存机制的业务对象Ӟ对于调用者而言Q应与BLL业务对象的调用保持一致。也x_我们需要引入一个新的对象去控制原来的BLL业务对象Q这个新的对象就是Proxy模式中的代理对象?/p>
以PetShop.BLL.Product业务对象ZQPetShop为其建立了代理对象ProductDataProxyQƈ在GetProductByCategory(){方法中Q引入了~存机制Q例如: private static readonly int productTimeout = int.Parse(ConfigurationManager.AppSettings["ProductCacheDuration"]); if (!enableCaching) string key = "product_by_category_" + category; // Check if the data exists in the data cache // Create a AggregateCacheDependency object from the factory // Store the output in the data cache, and Add the necessary AggregateCacheDependency object 与业务逻辑层Product对象的GetProductsByCategory()Ҏ相比Q增加了~存机制。当~存内不存在相关数据ҎQ则直接调用业务逻辑层Product的GetProductsByCategory()Ҏ来获取数据,q将其与对应的AggregateCacheDependency对象一起存储在~存中?/p>
引入Proxy模式Q实C在缓存别上对业务对象的装Q增Z对业务对象的控制。由于暴露在对象外的Ҏ是一致的Q因而对于调用方而言Q调用代理对象与真实对象q没有实质的区别?/p>
从职责分M分层设计的角度分析,我更希望q些Proxy对象是被定义在业务逻辑层中Q而不像在PetShop的设计那P被划分到表示层UI中。此外,如果需要考虑E序的可扩展性与可替换性,我们q可以ؓ真实对象与代理对象徏立统一的接口或抽象cR然而,单以PetShop的表C层调用来看Q采用静态类与静态方法的方式Q或许更为合理。我们需要}讎ͼ“q度设计”是Y件设计的警戒Uѝ?/p>
如果需要对UI层采用缓存机Ӟ应用程序数据存攑ֈ~存中,可以调用这些代理对象。以ProductsControl用户控gZQ调用方式如下: productsList对象属于自定义的CustomListcdQ这是一个派生自System.Web.UI.WebControls.DataList控g的类Q它的DataSource属性可以接受IList集合对象?br />
不过在PetShop 4.0的设计中Q对于类gProductsControlcd的控件而言Q采用的~存机制是页输出~存。我们可以从ProductsControl.ascx面的Source代码中发现端倪: 与ASP.NET 1.x的页输出~存不同的是Q在ASP.NET 2.0中,为ASP.NET用户控g新引入了CachePolicy属性,该属性的cd为ControlCachePolicyc,它以~程方式实现了对ASP.NET用户控g的输出缓存设|。我们可以通过讄ControlCachePolicycȝDependency属性,来设|与该用h件相关的依赖,例如在ProductsControl用户控g中,q行如下的设|: 采用输出缓存,q且利用ControlCachePolicy讄输出~存Q能够将业务数据与整个页面放入到~存中。这U方式比起应用程序缓存而言Q在性能上有很大的提高。同Ӟ它又通过引入的SqlCacheDependencyҎ有效地避免?#8220;数据q期”的缺点,因而在PetShop 4.0中被q泛采用。相反,之前为Product、Category、Item业务对象建立的代理对象则?#8220;投闲散置”Q仅仅作ZU设计方法的展示?#8220;q存”与整个系l的源代码中?/p>
1、WEBQ表C层
2、BLLQ业务逻辑?br />
3、IDALQ数据访问层接口定义
4、ModelQ业务实?br />
5、DALFactoryQ数据层的抽象工?创徏反射)
6、SQLServerDALQSQLServer数据讉K?/ OracleDALQOracle数据讉K?
三、实现步?br />
1、创建ModelQ实C务实体?br />
2、创建IDALQ实现接口?br />
3、创建SQLServerDALQ实现接口里的方法?br />
4、增加web.config里的配置信息QؓSQLServerDAL的程序集?br />
5、创建DALFactoryQ返回程序集的指定类的实例?br />
6、创建BLLQ调用DALFactoryQ得到程序集指定cȝ实例Q完成数据操作方法?br />
7、创建WEBQ调用BLL里的数据操作Ҏ?/p>
public interface IPetShopCacheDependency
{
AggregateCacheDependency GetDependency();
}
public abstract class TableDependency : IPetShopCacheDependency
{
// This is the separator that's used in web.config
protected char[] configurationSeparator = new char[] { ',' };
protected TableDependency(string configKey)
{
string dbName = ConfigurationManager.AppSettings["CacheDatabaseName"];
string tableConfig = ConfigurationManager.AppSettings[configKey];
string[] tables = tableConfig.Split(configurationSeparator);
dependency.Add(new SqlCacheDependency(dbName, tableName));
}
public AggregateCacheDependency GetDependency()
{
return dependency;
}
}
<add key="CacheDatabaseName" value="MSPetShop4"/>
<add key="CategoryTableDependency" value="Category"/>
<add key="ProductTableDependency" value="Product,Category"/>
<add key="ItemTableDependency" value="Product,Category,Item"/>
{
public Product() : base("ProductTableDependency") { }
}
{
public static IPetShopCacheDependency CreateCategoryDependency()
{
return LoadInstance("Category");
}
public static IPetShopCacheDependency CreateProductDependency()
{
return LoadInstance("Product");
}
public static IPetShopCacheDependency CreateItemDependency()
{
return LoadInstance("Item");
}
private static IPetShopCacheDependency LoadInstance(string className)
{
string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];
string fullyQualifiedClass = path + "." + className;
return (IPetShopCacheDependency)Assembly.Load(path).CreateInstance(fullyQualifiedClass);
}
}
整个工厂模式的实现如?-3所C:
?-3 CacheDependency工厂
AggregateCacheDependency dependency = DependencyAccess.CreateCategoryDependency().GetDependency();
?-4 Facade模式
public static class DependencyFacade
{
private static readonly string path = ConfigurationManager.AppSettings["CacheDependencyAssembly"];
public static AggregateCacheDependency GetCategoryDependency()
{
if (!string.IsNullOrEmpty(path))
return DependencyAccess.CreateCategoryDependency().GetDependency();
else
return null;
}
public static AggregateCacheDependency GetProductDependency()
{
if (!string.IsNullOrEmpty(path))
return DependencyAccess.CreateProductDependency().GetDependency();
else
return null;
}
public static AggregateCacheDependency GetItemDependency()
{
if (!string.IsNullOrEmpty(path))
return DependencyAccess.CreateItemDependency().GetDependency();
else
return null;
}
}
{
Category category = new Category();
if (!enableCaching)
return category.GetCategory(categoryId).Name;
string data = (string)HttpRuntime.Cache[cacheKey];
if (data == null)
{
// 通过web.config的配|获取duration?
int cacheDuration = int.Parse(ConfigurationManager.AppSettings["CategoryCacheDuration"]);
// 如果~存中不存在该数据项Q则通过业务逻辑层访问数据库获取;
data = category.GetCategory(categoryId).Name;
// 通过Facadecd建AggregateCacheDependency对象;
AggregateCacheDependency cd = DependencyFacade.GetCategoryDependency();
// 数据项以及AggregateCacheDependency 对象存储到缓存中;
HttpRuntime.Cache.Add(cacheKey, data, cd, DateTime.Now.AddHours(cacheDuration), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}
return data;
}
{
protected void Page_Load(object sender, EventArgs e)
{
Page.Title = WebUtility.GetCategoryName(Request.QueryString["categoryId"]);
}
}
public static class ProductDataProxy
{
private static readonly bool enableCaching = bool.Parse(ConfigurationManager.AppSettings["EnableCaching"]);
public static IList
GetProductsByCategory(string category)
{
Product product = new Product();
return product.GetProductsByCategory(category);
IList data = (IList )HttpRuntime.Cache[key];
if (data == null)
{
data = product.GetProductsByCategory(category);
AggregateCacheDependency cd = DependencyFacade.GetProductDependency();
HttpRuntime.Cache.Add(key, data, cd, DateTime.Now.AddHours(productTimeout), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}
return data;
}
}
productsList.DataSource = ProductDataProxy.GetProductsByCategory(categoryKey);
<%@ OutputCache Duration="100000" VaryByParam="page;categoryId" %>
protected void Page_Load(object sender, EventArgs e)
{
this.CachePolicy.Dependency = DependencyFacade.GetProductDependency();
}
public interface ICategory {
/// <summary>
/// Method to get all categories
/// </summary>
/// <returns>Interface to Model Collection Generic of categories</returns>
IList<CategoryInfo> GetCategories();
/// <summary>
/// Get information on a specific category
/// </summary>
/// <param name="categoryId">Unique identifier for a category</param>
/// <returns>Business Entity representing an category</returns>
CategoryInfo GetCategory(string categoryId);
}
我知道IList<CategoryInfo>是范?
表示list中的item是CategoryInfo对象
请问Z么用IList<CategoryInfo>
用List<CategoryInfo>可以吗?
两者有什么区别?谢谢
没有什么区别,q样写灵zL大Q实现ilist接口的类很多Q你写成list后,也许以后你要Ҏ非list的,׃要改很多代码
举个例子
RenderControlToString(DataList L//Control C)
你认为是写controlq是写datalist代码的通用性高?
OOP~码原则:可能用接口~程
]]>
{
public static readonly string connectionString = ConfigurationManager.ConnectionStrings["SqlConnString"].ConnectionString;
SqlConnection conn;
#region open SqlConnection
public static void Open() {
conn = new SqlConnection(connectionString);
if (conn.State != ConnectionState.Open)
conn.Open();
}
#endregion
#region close SqlConnection
public static void Close() {
if (conn != null)
{
conn.Close();
conn.Dispose();
}
}
#endregion
#region prepare SqlCommand
private static void PrepareCommand(SqlCommand cmd, CommandType cmdType, string cmdText, SqlParameter[] cmdParms) {
Open();
cmd.Connection = conn;
cmd.CommandType = cmdType;
cmd.CommandText = cmdText;
if (cmdParms != null)
{
foreach (SqlParameter parm in cmdParms)
cmd.Parameters.Add(parm);
}
}
#endregion
#region parm cache
/*
使用一个哈希表来保存缓存的参数 只缓存参数名
哈希表的特点:一个键对应一个值key对value(为object需要类型{? 不能出现两个相同的键 否则error
下面的哈希表parmCache定义为static即一ơ定义全局使用
所以可能会出现有h在读的时候,有h在写Q一般会用Lock像Asp中用Application["count"]来统计点L一?br />
要先锁后解锁
?net框架提供了Synchroized sync和syncroize中文意?同步Q同时发?br />
来提供这一操作
*/
private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable());
public static void CacheParameters(string cacheKey, params SqlParameter[] commandParameters) {
parmCache[cacheKey] = commandParameters;
}
/*
1、ؓ何要克隆?Z不直接return cachedParms
有一个参数数l?br />
SqlParameter[] parms={
new SqlParameter("@num1",SqlDbType.Int,4),
new SqlParameter("@num2",SqlDbType.Int,4)
}
~存该数l?br />
用户a和b都执行插入操?br />
a用户插入?,1
b用户插入?,2
如果不用克隆的话Q参数数l只有一?br />
?个用户需要根据不同的情况赋于不同的?br />
所以就用了克隆?br />
2?ICloneable)cachedParms[i]先将HashTable转ؓICloneableq样HashTable具有了Clone()克隆Ҏ?br />
克隆一份后是什么类型呢Q,当然要强制{化ؓ(SqlParameter)?br />
最后将它赋值给clonedParms[i]
*/
public static SqlParameter[] GetCachedParameters(string cacheKey) {
SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];
if (cachedParms == null)
return null;
SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length];
for (int i = 0; i < cachedParms.Length; i++)
clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone();
return clonedParms;
}
#endregion
//below method support sqltext and procedure
#region ExecuteReader
/*
parms的作用,q也是一个知识点
举例:
ExecuteReader(*,*,null)成功q行
ExecuteReader(*,*,new SqlParameter(*))成功q行
ExecuteReader(*,*,new SqlParameter(*),new SqlParameter(*))成功q行
ExecuteReader(*,*,{new SqlParameter(*),new SqlParameter(*),})成功q行
它让参数cd和参CCQ?br />
q可l了不是一般的好处Q你不必为SqlParameter和SqlParameter[]q行重蝲Q写上两个函?br />
又ؓnull写上一个函敎ͼ因ؓnull会不明确调用SqlParameter的函数还是SqlParameter[]的函?br />
啥你不知道我在说什么,打屁?那回ȝ看c++的函数重?br />
*/
public static SqlDataReader ExecuteReader(CommandType cmdType, string cmdText, params SqlParameter[] commandParameters) {
SqlCommand cmd = new SqlCommand();
try {
PrepareCommand(cmd, null, cmdType, cmdText, commandParameters);
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return rdr;
}
catch {
Close();
throw;
}
}
#endregion
#region ExecuteNonQuery
public static void ExecuteNonQuery(CommandType cmdType, string cmdText, params SqlParameter[] commandParameters) {
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, cmdType, cmdText, commandParameters);
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
Close();
}
#endregion
#region ExecuteScalar
public static object ExecuteScalar(CommandType cmdType, string cmdText, params SqlParameter[] commandParameters) {
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
Close();
return val;
}
#endregion
}
}