MemoryCache在MVC中的用途是什么?

Joh*_*Doe 11 c# asp.net-mvc

我对MemoryCache的正确使用感到困惑.

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

我有一个实例,我不想使用数据存储来填充和保存我的视图中的数据.我开始使用MemoryCache工作正常,但我开始怀疑这是否是正确的方法.

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

Nig*_*888 26

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

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


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

是.

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

是.如果您的视图使用相同的数据.或者,如果您的_Layout.cshtml页面上有需要缓存的数据,则可以在全局过滤器中完成.

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

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

缓存经常更改一两秒的数据也是一个好主意,以防止大量并发请求成为对数据库的拒绝服务攻击.

缓存取决于唯一键.通过使用户的名称或密钥的ID部分,可以将个人用户信息存储在缓存中.

var key = "MyFavoriteItems-" + this.User.Identity.Name;
Run Code Online (Sandbox Code Playgroud)

警告:仅当您具有单个Web服务器时,此方法才有效.它不会扩展到多个Web服务器.会话状态(用于个人用户存储器存储)是一种更具可扩展性的方法.但是,会话状态并不总是值得权衡.


典型的缓存模式

请注意,尽管MemoryCache是线程安全的,但将它与数据库调用结合使用可以使操作跨线程.如果没有锁定,可能会向数据库发出多个查询,以便在缓存过期时重新加载缓存.

因此,您应该使用双重检查的锁定模式来确保只有一个线程完成从数据库重新加载缓存.

假设您有一个列表,浪费每个请求,因为每个用户在到达特定页面时都需要列表.

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

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

注意:一种常见的方法是使用装饰器模式使缓存与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;
}
Run Code Online (Sandbox Code Playgroud)

因此,您调用GetCachedStateOrProvinceList它将自动从缓存中获取列表,如果未缓存,则会自动将数据库中的列表加载到缓存中.只允许一个线程调用数据库,其余线程将一直等到填充缓存,然后一旦可用就从缓存中返回列表.

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