当访问内存时,请务必使用上述模式。下面的这个模式,咋一看好像和上面的模式一模一样,但是有一个细微的区别,它存在一个race condition(可以理解为不易察觉的隐式缺陷)。race condition很难调试,因为它只是偶尔发生,而且再次发生的可能性也小。如下:
if (Cache["key"] == null) { Cache.Insert(key, BllMethodToGetInstance(), ...); } return Cache["key"];
再一个就是,上述模式不是在局部变量里存储缓存条目的引用,而是在条件语句里直接访问数据,在return语句里直接返回数据。设想这种情况,开始运行代码时Cache["key"]是non-null的,但在运行return语句前,系统将其从内存里清除掉,那么代码就会返回一个null值,而不是我们期望的某种类型的对象。
注意:如果仅仅是对data cache进行读或写访问,你没有必要进行同步访问(synchronize thread access);当然,如果你需要对内存里的数据进行多重操作(multiple operations),你还是应该实施锁定(lock),或其它的机制。
如果要从data cache里清除某个条目,可以用Remove方法,比如:
Cache.Remove(key);
第三步:从ProductsCL类返回产品信息
在本文,我们要在ProductsCL类里用2个方法来返回产品信息: GetProducts()和 GetProductsByCategoryID(categoryID). 和业务逻辑层里的ProductsBL类相似,缓存层里的GetProducts()方法返回一个Northwind.ProductsDataTable对象,来获取所有产品的信息;而GetProductsByCategoryID(categoryID)方法返回的是某个特定类别的所有产品。
如下的代码是ProductsCL类里的部分方法:
[System.ComponentModel.DataObject] public class ProductsCL { private ProductsBLL _productsAPI = null; protected ProductsBLL API { get { if (_productsAPI == null) _productsAPI = new ProductsBLL(); return _productsAPI; } } [System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Select, true)] public Northwind.ProductsDataTable GetProducts() { const string rawKey = "Products"; // See if the item is in the cache Northwind.ProductsDataTable products = _ GetCacheItem(rawKey) as Northwind.ProductsDataTable; if (products == null) { // Item not found in cache - retrieve it and insert it into the cache products = API.GetProducts(); AddCacheItem(rawKey, products); } return products; } [System.ComponentModel.DataObjectMethodAttribute(DataObjectMethodType.Select, false)] public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID) { if (categoryID < 0) return GetProducts(); else { string rawKey = string.Concat("ProductsByCategory-", categoryID); // See if the item is in the cache Northwind.ProductsDataTable products = _ GetCacheItem(rawKey) as Northwind.ProductsDataTable; if (products == null) { // Item not found in cache - retrieve it and insert it into the cache products = API.GetProductsByCategoryID(categoryID); AddCacheItem(rawKey, products); } return products; } } }
首先,注意运用到类(class)和方法(methods)上的属性 DataObject和 DataObjectMethodAttribute ;这些属性服务于ObjectDataSource的设置向导,指出那些类和方法应该出现在向导的设置步骤里。因为ObjectDataSource控件要在表现层访问这些类和方法,所以我添加了这些属性,方便向导设置。关于这些属性及其作用,请参阅本教程第2章《创建一个业务逻辑层》。
在GetProducts() 和 GetProductsByCategoryID(categoryID)方法里,GetCacheItem(key)返回的数据赋值给一个局部变量。GetCacheItem(key)方法根据指定的key值在内存查找对应的缓存条目;如果没找到,则用ProductsBLL类里相应的方法来检索数据,并用AddCacheItem(key, value)方法将获取的数据缓存到内存。
GetCacheItem(key) 和 AddCacheItem(key, value)方法分别对data cache进行读、写操作。GetCacheItem(key)相对简单,它根据传入的key值,从Cache类返回数据,如下:
private object GetCacheItem(string rawKey) { return HttpRuntime.Cache[GetCacheKey(rawKey)]; } private readonly string[] MasterCacheKeyArray = {"ProductsCache"}; private string GetCacheKey(string cacheKey) { return string.Concat(MasterCacheKeyArray[0], "-", cacheKey); }
GetCacheItem(key)并没有直接使用我们提供的key值,而是调用GetCacheKey(key)方法,因为该方法根据“ProductsCache-”返回key;在上述代码中,MasterCacheKeyArray用于存储字符串“ProductsCache”。当然,AddCacheItem(key, value)方法也会用到MasterCacheKeyArray,我们稍后会看到。