为什么要使用System.Runtime.Caching或System.Web.Caching而不是静态变量?

32

长期听众 - 首次来电。我希望能得到一些建议。我一直在阅读有关在.NET中使用System.Web.Caching和System.Runtime.Caching的缓存。 我想知道除了使用带锁的静态变量之外,我还能获得什么其他好处。我的当前(简单的)缓存方法如下:

public class Cache
{
    private static List<Category> _allCategories;
    private static readonly object _lockObject = new object();

    public static List<Category> AllCategories
    {
        get
        {
            lock (_lockObject)
            {
                if (_allCategories == null)
                {
                    _allCategories = //DB CALL TO POPULATE
                }
            }
            return _allCategories;
        }
    }
}

除了过期时间(我不想让它过期),我不知道使用内置缓存的好处在哪里。

也许对于不适用于我的更复杂的缓存场景有好处,或者我只是漏掉了什么(这不是第一次)。

那么,如果我想要一个永不过期的缓存,使用缓存的优势是什么?静态变量不能做到这一点吗?


我认为你的锁定块应该包括if语句。否则,可能会出现它仍然从数据库中加载超过一次的情况。 - row1
1
你是对的 - 抱歉 - 写一个简单的例子时有些匆忙。我已经在原问题中进行了更改。 - sfsr
3个回答

24
首先,Xaqron提出了一个很好的观点,你所谈论的可能并不符合缓存的定义。实际上,它只是一个懒加载的全局可访问变量。这没关系:作为一个实用的程序员,在没有真正受益的情况下,强行实现完整的缓存是没有意义的。但是,如果你要使用这种方法,最好使用Lazy,让.NET 4来完成繁重的工作:
private static Lazy<IEnumerable<Category>> _allCategories
    = new Lazy<IEnumerable<Category>>(() => /* Db call to populate */);

public static IEnumerable<Category> AllCategories 
{ 
    get { return _allCategories.Value; } 
}

我冒昧地将类型更改为IEnumerable<Category>,以防止调用者认为他们可以向此列表添加内容。

话虽如此,每当您访问公共静态成员时,您就会错过面向对象编程提供的许多灵活性。我个人建议您:

  1. 将类重命名为CategoryRepository(或类似名称),
  2. 使该类实现一个ICategoryRepository接口,在接口上有一个GetAllCategories()方法,并且
  3. 将此接口构造注入到需要它的任何类中。

这种方法将使您能够单元测试应该处理所有类别的类,完全控制要测试哪些“类别”,而无需进行数据库调用。


1
我同意“cache”可能不是正确的词语,但是“懒加载全局可访问变量”并不那么好发音 :) 我之前没有听说过Lazy,现在每当我需要一个懒加载变量时,我都会使用它。 - sfsr

23

System.Runtime.Caching和System.Web.Caching都具有自动过期控制,可以基于文件更改SQL Server数据库更改或您自己实现的“更改提供程序”进行。 您甚至可以在缓存条目之间创建依赖关系。

请查看此链接以了解有关System.Caching命名空间的更多信息:

http://msdn.microsoft.com/en-us/library/system.runtime.caching.aspx

我提到的所有功能都在链接中得到了详细的记录。

使用静态变量需要手动控制过期时间,并且需要使用缓存命名空间中提供的文件系统监视器等工具。


2
哇 - 你们真快 :) 这是一个很好的观点。我完全可以看到在更复杂的情况下需要这样做 - 但是假设原始问题中的类别列表是一个固定列表(意味着更改将非常少),并且我不希望它过期 - 我认为维护该过期控制所需的额外开销实际上是使用静态变量的投票,因为在这种情况下它们的性能更高。顺便说一句 - 我想要进行这种讨论并且想要深入研究这些类型的事情 - 我并不想要固执己见 - 只是在提出反面观点。 - sfsr
3
如果您不需要框架提供的服务,我认为您不应将自己局限于它们。如果您不需要过期控制或缓存系统的其他服务,则不要使用它。但是,如果您需要这些服务,请勿重新发明轮子。当然,这是我的观点!有些人可能不同意。 - Miguel Angelo
我认为在你提供的情况下使用静态变量是一个完全有效的解决方案。=) - Miguel Angelo

4

除了过期时间(我不希望这个过期)……

如果您不关心名称的生命周期,那么它就不再是缓存了。

仍然使用Application(在您的情况下)和Session对象是存储应用程序级别和会话级别数据的最佳实践。


1
我认为你说的缓存不是正确的词 - 我只是在指DB结果保存在本地内存中,而不必每次需要时都要访问较慢的介质。至于应用程序对象 - 我对此持不同意见。这里引用的MSDN文章 链接 表明它更适用于经典ASP迁移。 - sfsr
ASP经典版基于模块化编程。因此,为了在模块之间进行通信,应用程序范围变量(如C语言)是一种选择。您正在从数据库加载类别到内存中以便快速访问。如果您期望数据库中的更改反映在类别中,则需要根据某些标准(时间、触发器等)更新缓存类,然后它将成为一个有意义的缓存。缓存更多地涉及到繁重和冗长的操作,例如在我的电脑中查看使用的驱动器和可用空间。在股票市场网站上,每个请求计算一年的变化是不明智的。您可以每天执行一次。 - Xaqron

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