在ASP.NET2.0中操作数据之五十七:在分层架构中缓存数据作者:heker2007字体:[增加减小]类型:转载时间:2016-05-17我要评论上一篇文章我们介绍了ASP.NET2.0中使用ObjectDataSource在视图层缓存数据,缺点是不言而喻的,为了达到低耦合,本文介绍如何在三层架构中使用缓存技术来缓存数据。导言:正如前面章节所言,缓存ObjectDataSource的数据只需要简单的设置一些属性。然而,它是在表现层对数据缓存,这就与ASP.NETpage页面缓存策略(cachingpolicies)紧密的耦合(tightlycouples)起来。我们对体系机构分层的原因之一便是打破这种耦合。拿业务逻辑层为例,将业务逻辑从ASP.NET页面脱离出来;而数据访问层将数据访问的细节ASP.NET页面脱离出来。从某种意义来说,将业务逻辑和数据访问细节脱离出来是首先,这样的话使系统更易读、易维护、易修改,便于按模块分工—比如,表现层的开发者对数据库的细节不甚了解也不妨碍其开发工作。当然,将缓存策略从表现层脱离出来也有类似的好处。本文我们将对层次机构进行扩充,新添一个缓存层(CachingLayer,简称CL)以实施缓存策略。该缓存层包括一个ProductsCL类,该类用类似GetProducts(),GetProductsByCategoryID(categoryID)等方法来访问产品信息。调用这些方法时先从内存检索数据,如果内存为空则调用业务逻辑层BLL里的ProductsBLL类的相应方法,再从数据访问层DAL返回获取的数据。该ProductsCL类的方法从业务逻辑层BLL获取数据后先对数据缓存后再返回。如图1所示,缓存层CL位于表现层和业务逻辑层。图1:在我们的体系结构中缓存层(CL)是单独的一层第一步:创建缓存层的类在本文,我们创建的缓存层仅仅包含一个ProductsCL类,它只有几个方法。完整的缓存层还应该包含CategoriesCL,EmployeesCL,和SuppliersCL类。有了业务逻辑层BLL和数据访问层DAL,缓存层完全可以当成一个单独的类库工程(ClassLibraryproject),不过我们将它作为App_Code文件夹里的一个类来处理。为了更好的将缓存层类和DAL类、BLL类区分开,我们在App_Code文件夹里创建一个新的子文件夹。在资源管理器里右击App_Code文件夹,选择“新文件夹”,命名为CL,在里面添加新类ProductsCL.cs图2:添加名为CL的文件夹和名为ProductsCL.cs的类跟BLL里的ProductsBLL类一样,ProductsCL类应该包含相同的数据访问和修改方法。不过在本文,我们只创建GetProducts()方法(在第3步)和GetProductsByCategoryID(categoryID)方法(在第4步)。你可以在空闲的时候对ProductsCL类进行完善,并创建相应的CategoriesCL,EmployeesCL和SuppliersCL类第二步:对DataCache进行读和写ObjectDataSource的缓存属性使用ASP.NETdatacache来存储从BLL获取的数据。要访问datacache,可以从ASP.NET页面的code-behindclasses类或体系结构层(architecture)的类来访问。要通过ASP.NET页面的code-behindclasses类对datacache进行读写,可使用如下模式:?12//Readfromthecache(读)objectvalue=Cache[key];?12345//Addanewitemtothecache(写)Cache[key]=value;Cache.Insert(key,value);Cache.Insert(key,value,CacheDependency);Cache.Insert(key,value,CacheDependency,DateTime,TimeSpan);Cacheclass类的Insert方法可以有很多的重载。Cache[key]=value和Cache.Insert(key,value)是相同的,都是向cache添加一个条目(item),不过没有指定expiry(可以理解为缓存持续时间)。更具代表性的是,在我们向cache添加条目的时候指定一个expiry,它要么是dependency(从属体),要么是time-basedexpiry,又或者两者兼而有之,比如上面的最后2个表达式。如果所需的数据存储在内存的话,首先调用缓存层的方法返回数据。如果不在内存的话就调用BLL里相应的方法。数据先缓存再返回。就像下面的流程表解析的一样:图3:如果数据存在于内存的话就调用缓存层的方法。上图的流程可用如下的模式:?1Typeinstance=Cache[key]asType;234567if(instance==null){instance=BllMethodToGetInstance();Cache.Insert(key,instance,...);}returninstance;其中,Type是缓存在内存中的数据的类型——具体到本文,也就是Northwind.ProductsDataTable;此外,key用于唯一地标识缓存的每一个条目。如果指定了key值的那个条目不在内存中,那么instance就为null,然后用BLL类的某恰当的方法来检索数据,将获得的数据缓存到内存。将instance返回后,它将包含一个对数据的引用(referencetothedata),数据要么来自内存,要么是BLL类的返回数据。当访问内存时,请务必使用上述模式。下面的这个模式,咋一看好像和上面的模式一模一样,但是有一个细微的区别,它存在一个racecondition(可以理解为不易察觉的隐式缺陷)。racecondition很难调试,因为它只是偶尔发生,而且再次发生的可能性也小。如下:?12345if(Cache[key]==null){Cache.Insert(key,BllMethodToGetInstance(),...);}returnCache[key];再一个就是,上述模式不是在局部变量里存储缓存条目的引用,而是在条件语句里直接访问数据,在return语句里直接返回数据。设想这种情况,开始运行代码时Cache[key]是non-null的,但在运行return语句前,系统将其从内存里清除掉,那么代码就会返回一个null值,而不是我们期望的某种类型的对象。注意:如果仅仅是对datacache进行读或写访问,你没有必要进行同步访问(synchronizethreadaccess);当然,如果你需要对内存里的数据进行多重操作(multipleoperations),你还是应该实施锁定(lock),或其它的机制。如果要从datacache里清除某个条目,可以用Remove方法,比如:?1Cache.Remove(key);第三步:从ProductsCL类返回产品信息在本文,我们要在ProductsCL类里用2个方法来返回产品信息:GetProducts()和GetProductsByCategoryID(categoryID).和业务逻辑层里的ProductsBL类相似,缓存层里的GetProducts()方法返回一个Northwind.ProductsDataTable对象,来获取所有产品的信息;而GetProductsByCategoryID(categoryID)方法返回的是某个特定类别的所有产品。如下的代码是ProductsCL类里的部分方法:?12345678910111213141516171819202122232425262728[System.ComponentModel.DataObject]publicclassProductsCL{privateProductsBLL_productsAPI=null;protectedProductsBLLAPI{get{if(_productsAPI==null)_productsAPI=newProductsBLL();return_productsAPI;}}[System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Select,true)]publicNorthwind.ProductsDataTableGetProducts(){conststringrawKey=Products;//SeeiftheitemisinthecacheNorthwind.ProductsDataTableproducts=_GetCacheItem(rawKey)asNorthwind.ProductsDataTable;if(products==null){//Itemnotfoundincache-retrieveitandinsertitintothecacheproducts=API.GetProducts();AddCacheItem(rawKey,products);}returnproducts;}[System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Select,false)]publicNorthwind.ProductsDataTableGetProductsByCategoryID(intcategoryID){if(categoryID0)returnGetProducts();else29303132333435363738394041424344454647484950515253545556{stringrawKey=string.Concat(ProductsByCategory-,categoryID);//SeeiftheitemisinthecacheNorthwind.ProductsDataTableproducts=_GetCacheItem(rawKey)asNorthwind.ProductsDataTable;if(products==null){//Itemnotfoundincache-retrieveitandinsertitintothecacheproducts=API.GetProductsByCategoryID(categoryID);AddCacheItem(rawKey,products);}returnproducts;}}}首先,注意运用到类(class)和方法(methods)上的属性DataObject和DataObjectMethodAttribute;这些属性服务于ObjectDataSource的设置向导,指出那些类和方法应该出现在向导的设置步骤里。因为ObjectDataSource控件要在表现层访问这些类和方法,所以我添加了这些属性,方便向导设置。关于这些属性及其作用,请参阅本教程第2章《创建一个业务逻辑层》。在GetProducts()和GetProductsByCategoryID(categoryID)方法里,GetCacheItem(key)返回的数据赋值给一个局部变量。GetCacheItem(key)方法根据指定的key值在内存查找对应的缓存条目;如果没找到,则用ProductsBLL类里相应的方法来检索数据,并用AddCacheItem(key,value)方法将获取的数据缓存到内存。GetCacheItem(key)和AddCacheItem(key,value)方法分别对datacache进行读、写操作。GetCacheItem(key)相对简单,它根据传入的key值,从Cache类返回数据,如下:?12345678910p