What is the purpose of MemoryCache in MVC?

前端 未结 1 2005
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-13 16:03

I\'m a bit confused on the proper usage of MemoryCache.

Should/can it be used to load static information to save on repeated calls? Should/can it be used to persist

相关标签:
1条回答
  • 2020-12-13 16:31

    First of all, MemoryCache is part of the System.Runtime.Caching namespace. It can be used by MVC applications, but it is not limited to MVC applications.

    NOTE: There is also a System.Web.Caching namespace (much older) that can only be used with the ASP.NET framework (including MVC).


    Should/can it be used to load static information to save on repeated calls?

    Yes.

    Should/can it be used to persist data on a view across several action methods?

    Yes. If your views use the same data it can. Or if you have data that is on your _Layout.cshtml page that needs caching, it could be done in a global filter.

    what happens if I have several users on the same page using the same MemoryCache?

    Caching is shared between all users by default. It is specifically meant for keeping data in memory so it doesn't have to be fetched from the database on every request (for example, a list of state names on a checkout page for populating a dropdown for all users).

    It is also a good idea to cache data that changes frequently for a second or two to prevent a flood of concurrent requests from being a denial-of-service attack on your database.

    Caching depends on a unique key. It is possible to store individual user information in the cache by making the user's name or ID part of the key.

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

    Warning: This method works only if you have a single web server. It won't scale to multiple web servers. Session state (which is meant for individual user memory storage) is a more scalable approach. However, session state is not always worth the tradeoffs.


    Typical Caching Pattern

    Note that although MemoryCache is thread-safe, using it in conjunction with a database call can make the operation cross threads. Without locking, it is possible that you might get several queries to the database to reload the cache when it expires.

    So, you should use a double-checked locking pattern to ensure only one thread makes it through to reload the cache from the database.

    Let's say you have a list that is wasteful to get on every request because every user will need the list when they get to a specific page.

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

    To cache the result of this query, you can add another method with a double-checked locking pattern and use it to call your original method.

    NOTE: A common approach is to use the decorator pattern to make the caching seamless to your 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;
    }
    

    So, you call the GetCachedStateOrProvinceList and it will automatically get the list from the cache and if it is not cached will automatically load the list from the database into the cache. Only 1 thread will be allowed to call the database, the rest will wait until the cache is populated and then return the list from the cache once available.

    Note also that the list of states or provinces for each country will be cached individually.

    0 讨论(0)
提交回复
热议问题