【问题标题】:What is the purpose of MemoryCache in MVC?MVC 中 MemoryCache 的作用是什么?
【发布时间】:2016-04-15 12:13:59
【问题描述】:

我对 MemoryCache 的正确使用有点困惑。

应该/可以用来加载静态信息以节省重复调用吗? 是否应该/可以使用它跨多个操作方法在视图上持久保存数据?

我有一个实例,我不想使用数据存储在我的视图中填充和持久化数据。我开始使用运行良好的 MemoryCache,但是我开始质疑这是否是正确的方法。

我担心的是,如果我在同一页面上有多个用户使用相同的 MemoryCache,会发生什么?

【问题讨论】:

    标签: c# asp.net-mvc


    【解决方案1】:

    首先,MemoryCacheSystem.Runtime.Caching 命名空间的一部分。它可以被MVC应用程序使用,但不限于MVC应用程序。

    注意:还有一个 System.Web.Caching 命名空间(旧得多)只能与 ASP.NET 框架(包括 MVC)一起使用。


    应该/可以用来加载静态信息以节省重复调用吗?

    是的。

    应该/可以用它来跨多个操作方法在视图上持久化数据吗?

    是的。如果您的视图使用相同的数据,它可以。或者,如果您的 _Layout.cshtml 页面上有需要缓存的数据,可以在 global filter 中完成。

    如果我在同一页面上有多个用户使用相同的 MemoryCache,会发生什么情况?

    默认情况下,缓存在所有用户之间共享。它专门用于将数据保存在内存中,因此不必在每次请求时都从数据库中获取数据(例如,结帐页面上的状态名称列表,用于填充所有用户的下拉列表)。

    最好将频繁更改的数据缓存一两秒钟,以防止大量并发请求成为对数据库的拒绝服务攻击。

    缓存依赖于唯一键。通过将用户名或 ID 作为键的一部分,可以将单个用户信息存储在缓存中。

    var key = "MyFavoriteItems-" + this.User.Identity.Name;
    

    警告:此方法仅适用于您只有一个网络服务器的情况。它不会扩展到多个 Web 服务器。 Session state(用于个人用户内存存储)是一种更具可扩展性的方法。然而,会话状态并不总是值得tradeoffs


    典型的缓存模式

    请注意,尽管MemoryCache 是线程安全的,但将它与数据库调用结合使用可以使操作跨线程。如果不加锁,您可能会收到多个对数据库的查询,以便在缓存过期时重新加载缓存。

    因此,您应该使用double-checked locking 模式来确保只有一个线程能够通过以从数据库重新加载缓存。

    假设您有一个列表,因为每个用户在到达特定页面时都需要该列表,因此对于每个请求来说都是浪费的。

    public IEnumerable<StateOrProvinceDto> GetStateOrProvinceList(string country)
    {
        // Query the database to get the data...
    }
    

    要缓存此查询的结果,您可以添加另一个具有双重检查锁定模式的方法,并使用它来调用您的原始方法。

    注意:一种常见的方法是使用decorator pattern 使缓存与您的 API 无缝。

    private ObjectCache _cache = MemoryCache.Default;
    private object _lock = new object();
    
    // NOTE: The country parameter would typically be a database key type,
    // (normally int or Guid) but you could still use it to build a unique key using `.ToString()`.
    public IEnumerable<StateOrProvinceDto> GetCachedStateOrProvinceList(string country)
    {
        // Key can be any string, but it must be both 
        // unique across the cache and deterministic
        // for this function.
        var key = "GetCachedStateList" + country;
    
        // Try to get the object from the cache
        var stateOrProvinceList = _cache[key] as IEnumerable<StateOrProvinceDto>;
    
        // Check whether the value exists
        if (stateOrProvinceList == null)
        {
            lock (_lock)
            {
                // Try to get the object from the cache again
               stateOrProvinceList = _cache[key] as IEnumerable<StateOrProvinceDto>;
    
                // Double-check that another thread did 
                // not call the DB already and load the cache
                if (stateOrProvinceList == null)
                {
                    // Get the list from the DB
                    stateOrProvinceList = GetStateOrProvinceList()
    
                    // Add the list to the cache
                    _cache.Set(key, stateOrProvinceList, DateTimeOffset.Now.AddMinutes(5));
                }
            }
        }
    
        // Return the cached list
        return stateOrProvinceList;
    }
    

    所以,您调用GetCachedStateOrProvinceList,它会自动从缓存中获取列表,如果没有缓存,则会自动将列表从数据库加载到缓存中。仅允许 1 个线程调用数据库,其余线程将等待直到缓存被填充,然后在可用时从缓存中返回列表。

    另请注意,每个国家/地区的州或省列表将单独缓存。

    【讨论】:

    • “它应该/可以用于跨多个操作方法在视图上持久化数据吗?”实际上,答案是否定的。无法保证缓存将保留数据多长时间。即使它设置为不过期,缓存管理器也可以决定在下一个请求执行之前丢弃缓存的数据,因为内存不足。要在多个操作之间保留请求,请使用 HttpContext.Items
    • 为他提供丰富的信息。在我的情况下,我试图防止对数据进行再水化。这是因为我必须远程到服务器拉文件并将该文件分解为对象。我将不得不更好地重新考虑我的问题。
    • 我明白了。好吧,Sedat 在他的评论中提到的HttpContext.Items 字典是一个仅适用于当前请求的缓存。如果您的应用程序的多个部分使用相同的数据并且不想多次从数据库中获取它,这很有帮助。
    • @NightOwl888 比 ConcurrentDictionary 有什么好处(它似乎是线程安全和强类型的)?
    • @Kerry - ConcurrentDictionary 是一个较低级别的组件。它可用于创建缓存,但不包含任何高级缓存功能,例如项目过期或缓存依赖项。换句话说,它没有任何项目生命周期的概念。将项目放入 ConcurrentDictionary 后,它的生命周期与 ConcurrentDictionary 的生命周期一样长,或者直到您手动删除它为止。缓存可以根据固定或滑动时间限制和/或对文件或数据库依赖项或其他一些自定义规则的编辑来驱逐项目。
    猜你喜欢
    • 2020-05-03
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    • 2019-08-13
    • 2015-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多