在一个类库中正确实现缓存以供在ASP.NET应用程序中使用

9
我正在一个类库中实现缓存,该类库用于 asp.net 应用程序。
我将我的缓存对象创建为单例模式,并使用静态方法更新缓存,该方法实际上只是加载一个成员变量/属性与需要缓存的数据集合(当然有一些锁定逻辑)。我认为这是一个不错的方式,因为只需调用即可访问我的数据。
MyCacheObject.Instance.MyDataCollection

我正在创建一个新的缓存对象来存储大量数据,根据某些键进行分区。我的意思是我正在创建一个新的缓存,但这个缓存不会一次性加载所有数据,而是为每个访问的键存储一个集合。
MyOtherCacheObject.Instance.MyOtherDataCollection(indexkey)

这次提出了垃圾回收的问题。由于我存储了大量数据,如果它突然被gc清理掉,那不是很浪费吗?因为它只是一个单例模式,没有任何保证数据会留在缓存中。
所以我的问题是 - 实现缓存以处理这种情况的最佳实践是什么?我真的不喜欢一个复杂的解决方案,我知道System.Web中有缓存,但这似乎有些“离谱”,因为这只是一个类库,你怎么看?
2个回答

9
在我看来,最好的解决方案应具备以下特点:
  • 使用平台提供的可用缓存服务,尽量避免编写自己的缓存实现。

  • 不将类库与System.Web耦合,以保持层之间的一致性。

  • 但如果类库运行在ASP.NET应用程序中,则解决方案不应该要求引入另一个缓存实现(例如Enterprise Library Caching Application Block),这需要额外的配置和设置。

因此,我会使用IoC策略,以允许类库基于其运行环境使用不同的缓存实现。
假设您将抽象缓存契约定义为:
public interface ICacheService 
{
    AddItem(...);
}

您可以基于System.Web提供一个实现:
public AspNetBasedCacheService : ICacheService
{
    AddItem(...)
    {
        // Implementation that uses the HttpContext.Cache object
    }
 }

然后将该实现“发布”为单例。请注意,与您原始的方法不同之处在于,单例只是对基于ASP.NET缓存服务的实现的引用,而不是完整的“缓存对象”。

public class CacheServiceProvider 
{
    public static ICacheService Instance {get; set;}

}

您需要通过进行延迟初始化或应用程序启动(在Global.asax.cs中)来初始化缓存实现。

每个域组件都能够使用已发布的缓存服务,而不知道它是基于System.Web实现的。

// inside your class library:
ICacheService cache = CacheServiceProvider.Instance;
cache.AddItem(...);

我同意这可能不是最简单的解决方案,但我希望利用ASP.NET缓存实现而不牺牲代码解耦和灵活性。

我希望我理解了你的问题。


我喜欢你的想法(尽管我会尽力避免控制反转)。不过你提出了一些好的点子,我可以借鉴。谢谢。 - Per Hornshøj-Schierbeck
如果执行了设定操作,您还可以添加一个备份变量并对该变量执行锁定。我的直觉告诉我这可能会防止一些线程问题。 - Kees C. Bakker

1

只要缓存仍然持有对数据的引用,数据就不会被垃圾回收。

此外,永远不要使用单例模式。


但我担心缓存对象会被垃圾回收,而不是它缓存的数据。 - Per Hornshøj-Schierbeck
1
除了使用单例来保存我的缓存,有什么更好的方法吗? - Per Hornshøj-Schierbeck
1
此外,永远不要使用单例模式。但是,对于接口实现仍然存在合法的使用情况(因为您无法拥有静态接口)。 - Marc Gravell
我的观点是那个引用在哪里?该引用仅在 asp.net 页面请求期间存在。假设该网站负载较重,可能会有至少一个引用同时存在,但这不是一个非常多用户的网站,那么谁来持有该引用呢? - Per Hornshøj-Schierbeck
听起来你的单例(顺便说一下,你永远不应该使用它)在这种情况下实际上是一个每页全局变量。 - 1800 INFORMATION
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接