Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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 data on a view across several action methods?

I have an instance where I don't want to use the data store to populate and persist the data across my view. I started using the MemoryCache which works fine, however I'm starting to question if that was the correct approach.

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
222 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...