今天遇到一个错误,描述如下:
缓存管理器:CacheManager _cache
if(!_cache.Contains(key))
{
    //...做一些加载缓存操作
    //_cache.Add(key, obj,  CacheItemPriority.Normal, new RefreshAction(),  new SlidingTime(TimeSpan.FromSeconds(1)));
}
return _cache[key];
在缓存加载时,使用了过期策略,为了方便测试,我使用了1秒钟就过期。

在加载缓存操作并Add后(理论上我确保了_cache中一定含有key值),_cache[key]却出现了null。
在程序中的可能性反映是:未将对象引用设置到对象的实例。

为什么会出现这样的错误呢?经研究发现,这是缓存过期策略造成的。即我写的RefreshAction类,此类继承了ICacheItemRefreshAction接口,用来在缓存过期时做一些处理。
[Serializable]
public class ItemCacheRefreshAction : ICacheItemRefreshAction
{
 public void Refresh(string removeKey, object expiredValue, CacheItemRemovedReason removalReason)
 {
  Item item = (Item)expiredValue;
  Console.WriteLine(item.Name + " " + item.Guid + "已过期,过期原因:" + removalReason);
 }
}
那么什么时候会命中这个回调呢?其实官方手册中已经有过描述,最常见的有两种情况会命中这个回调。
一. 在cache.config缓存配置文件中的扫描策略,其中expirationPollFrequencyInSeconds指定了扫描间隔,当时间达到时,就会命中这个回调,即会让缓存过期。
二. 在Add时,指定了过期策略,其中new SlidingTime(TimeSpan.FromSeconds(1))指定了在1秒钟后过期,当在上面第一条中的扫描器未到时间,而此缓存又过期时,再次访问缓存时,就会强行命中此回调,即让缓存过期。这是企业库缓存机制较隐蔽的陷阱。下面举个例子讲讲第二条。
1. 扫描器初始化,且时间假设为100分钟,为了测试第二条,我故意设了一个很大的间隔时间
2. Key = 1, Value = 1,我在Add时指定了1秒钟的过期时间
3. 第一次访问_cache[1],会进行缓存加载,然后正常出结果
4. 等待1秒钟以上
5. 第二次访问_cache[1]时,命中了上面的回调,缓存过期了。
由此我的错误就有了解释,我错误的使用了if(!_cache.Contains(key))来判断是否存在缓存,此时不会触发过期机制,而后我又直接返回了_cache[key],其实这时已经到了上面的第5步,即缓存过期了。于是我的null便产生了。

掌握了缓存过期策略,解决办法就比较简单了。
Object obj = _cache[key];
if(obj == null)
{
    //...做一些加载缓存操作
    //_cache.Add(key, obj,  CacheItemPriority.Normal, new RefreshAction(),  new SlidingTime(TimeSpan.FromSeconds(1)));
}
obj = _cache[key];
return obj;

用obj == null替换了Contains语句,其实就是保证过期的命中,其后再根据是否为null来加载缓存

Cache Application Block缓存过期机制需要深入研究,才能充分体现这个模块的易用和强大。 

为次我写了个简单的Demo,放在附件中点击下载此文件,下载后将.txt改为.rar解压即可。
注意修改cs文件中配置文件的路径E:\\cs\\CacheApplicationBlock\\cache.config

posted on 2007-10-27 23:02  宝气狗  阅读(519)  评论(0编辑  收藏  举报