缓存API,使用Cache对象 页面级和用户控件级输出缓存的确是一种可以迅速而简便地提高站点性能的方法,但是在ASP.NET中,缓存的真正灵活性和强大功能是通过Cache对象提供的。使用Cache对象,您可以存储任何可序列化的数据对象,基于一个或多个依赖项的组合来控制缓存条目到期的方式。这些依赖项可以包括自从某对象被缓存后经过的时间、自从某对象上次被访问后经过的时间、对文件或文件夹的更改以及对其他缓存对象的更改,在略作处理后还可以包括对数据库中特定表的更改。 在Cache中存储数据 在Cache中存储数据的最简单的方法就是使用一个键为其赋值,就像HashTable或Dictionary对象一样: Cache["key"] = "value"; 这种做法将在缓存中存储项,同时不带任何依赖项,因此它不会到期,除非缓存引擎为了给其他缓存数据提供空间而将其删除。要包括特定的缓存依赖项,可使用Add()或Insert()方法。其中每个方法都有几个重载。Add()和Insert()之间的唯一区别是,Add()返回对已缓存对象的引用,而Insert()没有返回值(在C#中为空,在VB中为Sub)。 示例 Cache.Insert("key", myXMLFileData, new System.Web.Caching.CacheDependency(Server.MapPath("users.xml"))); 该示例可将文件中的xml数据插入缓存,无需在以后请求时从文件读取。CacheDependency的作用是确保缓存在文件更改后立即到期,以便可以从文件中提取最新数据,重新进行缓存。如果缓存的数据来自若干个文件,还可以指定一个文件名的数组。 Cache.Insert("dependentkey", myDependentData, new System.Web.Caching.CacheDependency(new string[] {}, new string[] {"key"})); 该示例可插入键值为“key”的第二个数据块(取决于是否存在第一个数据块)。如果缓存中不存在名为“key”的键,或者如果与该键相关联的对象已到期或被更新,则“dependentkey”的缓存条目将到期。 Cache.Insert("key", myTimeSensitiveData, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero); 绝对到期:此示例将对受时间影响的数据缓存一分钟,一分钟过后,缓存将到期。注意,绝对到期和滚动到期(见下文)不能一起使用。 Cache.Insert("key", myFrequentlyAccessedData, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(1)); 动态滚动到期:此示例将缓存一些频繁使用的数据。数据将在缓存中一直保留下去,除非数据未被引用的时间达到了一分钟。注意,动态滚动到期和绝对到期不能一起使用。 更多选项 除了上面提到的依赖项,我们还可以指定项的优先级(依次为low、high、NotRemovable,它们是在System.Web.Caching.CacheItemPriority枚举中定义的)以及当缓存中的对象到期时调用的CacheItemRemovedCallback函数。大多数时候,默认的优先级已经足够了-缓存引擎可以正常完成任务并处理缓存的内存管理。CacheItemRemovedCallback选项考虑到一些很有趣的可能性,但实际上它很少使用。不过,为了说明该方法,我将提供它的一个使用示例: CacheItemRemovedCallback示例 System.Web.Caching.CacheItemRemovedCallback callback = new System.Web.Caching.CacheItemRemovedCallback (OnRemove);Cache.Insert("key",myFile,null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.Zero, System.Web.Caching.CacheItemPriority.Default, callback);. . .public static void OnRemove(string key, object cacheItem, System.Web.Caching.CacheItemRemovedReason reason){ AppendLog("The cached value with key '" + key + "' was removed from the cache. Reason: " + reason.ToString()); } 该示例将使用AppendLog()方法中定义的任何逻辑来记录缓存中的数据到期的原因。通过在从缓存中删除项时记录这些项并记录删除的原因,您可以确定是否在有效地使用缓存或者您是否可能需要增加服务器上的内存。注意,callback是一个静态(在VB中为Shared)方法,建议使用该方法的原因是,如果不使用它,保存回调函数的类的实例将保留在内存中,以支持回调(对static/Shared方法则没有必要)。 该特性有一个潜在的用处-在后台刷新缓存的数据,这样用户永远都不必等待数据被填充,但数据始终保持相对较新的状态。但实际上,此特性并不适用于当前版本的缓存API,因为在从缓存中删除缓存的项之前,不触发或不完成回调。因此,用户将频繁地发出尝试访问缓存值的请求,然后发现缓存值为空,不得不等待缓存值的重新填充。我希望在未来的ASP.NET版本中看到一个附加的回调,可以称为CachedItemExpiredBut NotRemovedCallback,如果定义了该回调,则必须在删除缓存项之前完成执行。 缓存数据引用模式 每当我们尝试访问缓存中的数据时,都应该考虑到一种情况,那就是数据可能已经不在缓存中了。因此,下面的模式应该普遍适用于您对缓存的数据的访问。在这种情况下,我们假定已缓存的数据是一个数据表。 public DataTable GetCustomers(bool BypassCache){ string cacheKey = "CustomersDataTable"; object cacheItem = Cache[cacheKey] as DataTable; if((BypassCache) || (cacheItem == null)) { cacheItem = GetCustomersFromDataSource(); Cache.Insert(cacheKey, cacheItem, null, DateTime.Now.AddSeconds(GetCacheSecondsFromConfig(cacheKey), TimeSpan.Zero); } return (DataTable)cacheItem;} 关于此模式,有以下几点需要注意: 1) 某些值(例如,cacheKey、cacheItem和缓存持续时间)是一次定义的,并且只定义一次。 2) 可以根据需要跳过缓存-例如,当注册一个新客户并重定向到客户列表后,最好的做法可能就是跳过缓存,用最新数据重新填充缓存,该数据包括新插入的客户。 3) 缓存只能访问一次。这种做法可以提高性能,并确保不会发生NullReferenceExceptions,因为该项在第一次被检查时是存在的,但第二次检查之前就已经到期了。 4) 该模式使用强类型检查。C#中的“as”运算符尝试将对象转换为类型,如果失败或该对象为空,则只返回null(空)。 5) 持续时间存储在配置文件中。在理想的情况下,所有的缓存依赖项(无论是基于文件的,或是基于时间的,还是其他类型的依赖项)都应该存储在配置文件中,这样就可以进行更改并轻松地测量性能。我还建议您指定默认缓存持续时间,而且,如果没有为所使用的cacheKey指定持续时间,就让GetCacheSecondsFromConfig()方法使用该默认持续时间。 与本文相关的代码示例(CachedDemo.msi,参见本书示例光盘)是一个helper类,它将处理上述所有情况,可以只书写一行或两行代码访问缓存的数据。 小结 缓存可以使应用程序的性能得到很大的提高,因此在设计应用程序以及对应用程序进行性能测试时应该予以考虑。应用程序总会或多或少地受益于缓存,当然有些应用程序比其他应用程序更适合使用缓存。对ASP.NET提供的缓存选项的深刻理解是任何ASP.NET开发人员应该掌握的重要技巧。

评论