ASP.NET Core - In Memory Caching

Caching is a technique of storing frequently used data in a temporary storage area. Caching improves performance and scalability. When we implement caching on data, the copy of data is stored in the temporary storage area. Hence, when the same data is requested next time, it is picked up from temporary storage area, loading it much faster than from the original source.
ASP.NET Core supports different kinds of caching such as In-Memory Cache, Distributed Cache and Response Cache. This article introduces the In-Memory Cache.
The In-Memory Cache stores data in the memory of Web Server where a web application is hosted. An application can be hosted on single Server or multiple Servers in a Server Farm. When an application is hosted on a Server, the In-Memory Cache works perfectly but when an application runs on the Server farm, then we should ensure that the sessions are sticky.
  • Server Farm
    It is the group of networked Servers which are interconnected and connected to a Central Management Server at a physical location. It provides the combined computing power of many Servers by simultaneously executing one or more applications or services. The Central Server has load-balancing software which manages the overall operations of these Servers, such as assigning processes, resource balancing, scheduling, security, updates and more. Hence, a web application can be hosted on multiple Servers and managed by the Server farm.

  • Sticky Session
    All application requests are redirected to the same physical Web Server for a particular client. Suppose, we have a web application which runs on 2 web Servers. Each Server has a separate physical box. These servers are interconnected by a load balancer. The load balancer decides which actual web Server should each request to go.

    Ex-  We have two web Servers - Server 1 and Server 2 and we have requested to access page1 and page2. Now, page1 is served by Server 1 and page 2 is served by Server 2. Thus, both the Servers create a separate session for both requests with the same client. There's no direct way of knowing what is there in the session object of the other.

    Now, here comes the role of sticky session. If the load balancer is instructed to use sticky sessions, all of the application interactions will happen on the same physical Server, even though other Servers are present. Thus, application session object will be the same throughout our entire interaction with this web application.
Configure In Memory Cache
First, create an empty ASP.NET Core project. As this project doesn’t hold default implementation of ASP.NET Core Cache, we will build an application step by step with ASP.NET Core Cache. The project.json file doesn't have any cache NuGet packages.
The InMemory cache which uses ImemoryCache interface requires NuGet package "Microsoft.Extensions.Caching.Memory". We have installed it in the application. The following code snippet is used for project.json file. 
  1. {  
  2.   "dependencies": {  
  3.     "Microsoft.NETCore.App": {  
  4.       "version""1.1.0",  
  5.       "type""platform"  
  6.     },  
  7.     "Microsoft.AspNetCore.Diagnostics""1.1.0",  
  8.     "Microsoft.AspNetCore.Mvc""1.1.1",  
  9.     "Microsoft.AspNetCore.Razor.Tools": {  
  10.       "version""1.0.0-preview2-final",  
  11.       "type""build"  
  12.     },  
  13.     "Microsoft.AspNetCore.Routing""1.1.0",  
  14.     "Microsoft.AspNetCore.Server.IISIntegration""1.1.0",  
  15.     "Microsoft.AspNetCore.Server.Kestrel""1.1.0",  
  16.     "Microsoft.AspNetCore.StaticFiles""1.1.0",  
  17.     "Microsoft.Extensions.Configuration.EnvironmentVariables""1.1.0",  
  18.     "Microsoft.Extensions.Configuration.Json""1.1.0",  
  19.     "Microsoft.Extensions.Logging""1.1.0",  
  20.     "Microsoft.Extensions.Logging.Console""1.1.0",  
  21.     "Microsoft.Extensions.Logging.Debug""1.1.0",  
  22.     "Microsoft.Extensions.Options.ConfigurationExtensions""1.1.0",  
  23.     "Microsoft.VisualStudio.Web.BrowserLink.Loader""14.1.0",  
  24.     "Microsoft.Extensions.Caching.Memory""1.1.0",  
  25.     "BundlerMinifier.Core""2.3.327"  
  26.   },  
  28.   "tools": {  
  29.     "Microsoft.AspNetCore.Razor.Tools""1.0.0-preview2-final",  
  30.     "Microsoft.AspNetCore.Server.IISIntegration.Tools""1.0.0-preview2-final"  
  31.   },  
  33.   "frameworks": {  
  34.     "netcoreapp1.0": {  
  35.       "imports": [  
  36.         "dotnet5.6",  
  37.         "portable-net45+win8"  
  38.       ]  
  39.     }  
  40.   },  
  42.   "buildOptions": {  
  43.     "emitEntryPoint"true,  
  44.     "preserveCompilationContext"true  
  45.   },  
  47.   "runtimeOptions": {  
  48.     "configProperties": {  
  49.       "System.GC.Server"true  
  50.     }  
  51.   },  
  53.   "publishOptions": {  
  54.     "include": [  
  55.       "wwwroot",  
  56.       "**/*.cshtml",  
  57.       "appsettings.json",  
  58.       "web.config"  
  59.     ]  
  60.   },  
  62.   "scripts": {  
  63.     "prepublish": [ "bower install""dotnet bundle" ],  
  64.     "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]  
  65.   }  
  66. }   
The In-Memory caching is a service called by dependency injection in the application, so we register it in the ConfigureServices method of Startup class, as per the following code snippet.
  1. public void ConfigureServices(IServiceCollection services)  
  2. {  
  3.     services.AddMvc();  
  4.     services.AddMemoryCache();  
  5. }   
Implement In-Memory Cache
To implement the In-Memory cache, we create a Controller named HomeController. This Controller holds the implementation of the In-Memory cache.
Now, we create ImemoryCache interface instance in the HomeController using constructor dependency injection. The following code snippet can be used for the same. 
  1. using Microsoft.AspNetCore.Mvc;    
  2. using Microsoft.Extensions.Caching.Memory;    
  3. using System;    
  5. namespace MemoryCacheApplication.Controllers    
  6. {    
  7.     public class HomeController : Controller    
  8.     {    
  9.         private readonly IMemoryCache memoryCache;    
  11.         public HomeController(IMemoryCache memoryCache)    
  12.         {    
  13.             this.memoryCache = memoryCache;    
  14.         }    
  15.     }    
  16. }  
Now, we need to create an action method named Index. This action method sets data in the cache. The following code snippet is for the same. 
  1. public IActionResult Index()  
  2. {  
  3.     DateTime currentTime;  
  4.     bool isExist = memoryCache.TryGetValue("CacheTime", out currentTime);  
  5.     if (!isExist)  
  6.     {                 
  7.         currentTime = DateTime.Now;                  
  8.         var cacheEntryOptions = new MemoryCacheEntryOptions()  
  9.             .SetSlidingExpiration(TimeSpan.FromSeconds(30));  
  11.         memoryCache.Set("CacheTime", currentTime, cacheEntryOptions);  
  12.     }  
  13.     return View(currentTime);  
  14. }   
The above code snippet has consumed some methods of InMemory cache service for reading and writing the data in the cache.
  1. TryGetValue
    This method reads value and assigns it in out parameter. It returns true if the value exists otherwise returns false.

  2. Set
    This method writes data in the cache. This method has three options - Cache Key name, data which is to be cached, and expiration option respectively.

  3. SetSlidingExpiration
    It sets cache expiration from absolute to sliding. When we make another request with expiration time, then it will be renewed with each response. Now, we create a View named "Index" under Views/Home folder for the Index action method as per the following code snippet.
    1. @model DateTime?  
    2. <div class="row">  
    3.     <div class="col-md-3">  
    4.         <h3> Current Time: @DateTime.Now.ToString()</h3>  
    5.     </div>  
    6.     <div class="col-md-3">  
    7.         <h3>Cached Time: @Model.Value.ToString()</h3>  
    8.     </div>         
    9. </div>   
The above code shows current time and cached time. Now, we will run the application and make another request within 30 seconds with the same client. The result shows as per the below figure.
in-memory cache 
Figure 1: Data from cache
The result shows that when we request within expiration time, then it returns cache data.
Read and Write In-Memory Cache
The In-Memory Cache provides another option to read and write data in the cache. There are two methods - Get and GetOrCreate.
  1. Get
    This method is used to get cached data. It takes cache key as a parameter and returns data stored based on this key.

  2. GetOrCreate
    If the data exists for a cache key, then this method reads that data and returns. If cache data for that cache key doesn’t exist, then it writes data in the cache.
Now, we will create another example in the same application while using these two methods. We have created View Model named CacheViewModel as per the following code snippet.
  1. using System;  
  2. namespace MemoryCacheApplication.Models    
  3. {    
  4.     public class CacheViewModel    
  5.     {    
  6.         public DateTime? FirstTime { getset; }    
  7.         public DateTime SecondTime { getset; }    
  8.     }    
  9. }  
Now, create Index action method in HomeController. This action method implements both cache In-Memory methods. See the code snippet. 
  1. public IActionResult Index()  
  2. {  
  3.     CacheViewModel model = new CacheViewModel  
  4.     {  
  5.         FirstTime = memoryCache.Get<DateTime?>("CacheTime"),  
  6.         SecondTime = memoryCache.GetOrCreate("CacheTime", entry =>  
  7.         {  
  8.             entry.SlidingExpiration = TimeSpan.FromSeconds(45);  
  9.             return DateTime.Now;  
  10.         })  
  11.     };  
  12.     return View(model);  
  13. }   
Now, we create a View named "Index" under Views/Home folder for the Index action method, as per the following code snippet. 
  1. @model MemoryCacheApplication.Models.CacheViewModel  
  2. <div class="row">  
  3.     <div class="col-md-3">  
  4.         <h3> Current Time: @DateTime.Now.ToString()</h3>  
  5.     </div>  
  6.     <div class="col-md-3">  
  7.         <h3> First Time: @(Model.FirstTime.HasValue?Model.FirstTime.ToString():"Cache is not available")</h3>  
  8.     </div>  
  9.     <div class="col-md-3">  
  10.         <h3>Second Time: @Model.SecondTime.ToString()</h3>  
  11.     </div>         
  12. </div>   
The above code shows the current time and cached time.
Now, we will run the application and make another request within 45 seconds with the same client. The result shows as per the below figure for both methods.
in-memory cache 
Figure 2: Show Caching Data
Remove Data from In-Memory Cache
The In-Memory cache can be evicted either automatically or manually. There are some cases when cache evicts automatically. These are
  1. Memory Pressure
    The IMemoryCache cache will evict cache entries under memory pressure unless the cache priority is set to CacheItemPriority.NeverRemove.

  2. Sliding Expiration
    We set the value in timespan for how long a cache entry can be inactive before removing it from the cache. If a request doesn't make for that period, then it removes automatically.
There is Remove method of IMemoryCache interface which removes the cache from the memory. We create Index action method which writes data in the cache if cache does not exist already. The following code snippet is used for the same. 
  1. [HttpGet]  
  2. public IActionResult Index()  
  3. {  
  4.     DateTime currentTime = memoryCache.GetOrCreate("CacheTime", entry =>  
  5.     {  
  6.         entry.SlidingExpiration = TimeSpan.FromSeconds(45);  
  7.         return DateTime.Now;  
  8.     });              
  9.     return View(currentTime);  
  10. }   
Now, we will create a View named Index under Views/Home folder for the Index action method as per following code snippet. 
  1. @model DateTime?  
  3. @using (Html.BeginForm("RemoveCache","Home"))  
  4. {  
  5.     <div class="row">  
  6.         <div class="col-md-3">  
  7.             <h3> Current Time: @DateTime.Now.ToString()</h3>  
  8.         </div>  
  9.         <div class="col-md-3">  
  10.             <h3> Cache Time: @(Model!=null ? @Model.ToString() : "Cache is not available")</h3>  
  11.         </div>  
  12.         <div class="col-md-3">  
  13.             <input type="submit" class="btn btn-danger" value="Remove" />  
  14.         </div>  
  15.     </div>  
  16. }   
As this View has Submit button which calls an action method named RemoveCache. This action method removes cached data and returns same View. The following code snippet is used for same. 
  1. [HttpPost]  
  2. public IActionResult RemoveCache()  
  3. {              
  4.     memoryCache.Remove("CacheTime");  
  5.     DateTime? currentTime = memoryCache.Get<DateTime?>("CacheTime");  
  6.     return View("Index",currentTime);  
  7. }   
Now, run the application. It shows both current time and cached data along with Remove button. When clicked on Remove button, it calls action method and removes the cached data.
in-memory cache 
Figure 3: Remove Cache Data
The In-Memory cache is used when the application is hosted on a single Server. It stores data on Server and improves application performance.
You can download the complete source code from MSDN sample, using the links, mentioned below.
  1. Rating Star Application in ASP.NET Core
  2. CRUD Operations in ASP.NET Core and Entity Framework Core
  3. Repository Pattern In ASP.NET Core
  4. Generic Repository Pattern in ASP.NET Core
  5. Onion Architecture In ASP.NET Core MVC
  6. NET Core MVC: Authentication and Role Based Authorisation with Identity
  7. NET Core MVC: Authentication and Claim Based authorization with Identity