Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the purpose of MemoryCache in MVC?

Tags:

c#

asp.net-mvc

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?

like image 731
John Doe Avatar asked Apr 15 '16 12:04

John Doe


People also ask

How does MemoryCache work?

Memory caching (often simply referred to as caching) is a technique in which computer applications temporarily store data in a computer's main memory (i.e., random access memory, or RAM) to enable fast retrievals of that data. The RAM that is used for the temporary storage is known as the cache.

What is MemoryCache C#?

In-Memory Cache is used for when you want to implement cache in a single process. When the process dies, the cache dies with it. If you're running the same process on several servers, you will have a separate cache for each server. Persistent in-process Cache is when you back up your cache outside of process memory.

Is MemoryCache per user?

The ASP.NET Session object is per user key/value storage, whereas MemoryCache is an application level key/value storage (values are shared among all users).

Is MemoryCache a singleton?

Note that the MemoryCache is a singleton, but within the process. It is not (yet) a DistributedCache. Also note that Caching is Complex(tm) and that thousands of pages have been written about caching by smart people.


1 Answers

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.

like image 66
NightOwl888 Avatar answered Sep 29 '22 06:09

NightOwl888