DotNetCurry Logo

InMemory Caching in ASP.NET MVC 6

Posted by: Mahesh Sabnis , on 1/28/2016, in Category ASP.NET MVC
Views: 14740
Abstract: InMemory Cache in ASP.NET MVC 6 and ASP.NET Core is one of the reliable ways to prevent an additional roundtrip to the data source for data that does not change frequently. This article provides an overview.

Caching enables you to store data in memory for rapid access. This way, when you access the data again, your applications can fetch data from the cache instead of making round trips to the data source. This improves performance and scalability. Caching also comes in handy when the data source is temporarily unavailable.

 

ASP.NET Caching in a Simple way

The implementation of caching in ASP.NET applications helps to eliminate unnecessary requests to an external data source server for data which does not change frequently. Caching makes a copy of the data to optimize the performance. This copy is maintained for a small duration or for a duration configured in your application. In earlier releases of ASP.NET, we were provided with different caching techniques e.g. Data Caching, OutputCaching, InMemory Caching, etc.

InMemory caching in ASP.NET MVC 6 and ASP.NET Core 1.0 is the simplest form of caching where the cache data is stored in the memory of the local web server. When the Web application is hosted on a single web server, then memory caching implemented for that web application, uses memory of that host server. However, in case if the ASP.NET Web Application is hosted in the Cloud on multiple host servers, then the in-memory cache will be different in different servers.

Note: ASP.NET Core 1.0 (previously known as ASP.NET 5) is a redesign of ASP.NET that we have been using all these years. In earlier versions of ASP.NET, Web API was provided as a separate Web API framework; but going forward in the ASP.NET Core 1.0 release, Web API is merged with ASP.NET MVC, termed as MVC 6. This provides an advantage of creating applications using HTML and API services, using a single framework.

Implementing InMemory Caching in ASP.NET MVC 6

We will implement this application using Visual Studio 2015 and ASP.NET MVC 6 and Core 1.0.

Step 1: Open Visual Studio 2015 and create a new ASP.NET Web Application, from New Project window. Name it as InMemoryCaching and click on the OK button. This will show a New ASP.NET Project window. Select Empty from ASP.NET Template to create a new project.

Step 2: In the project, open project.json and since we are creating an MVC application, add the following dependency references in the dependencies section.

"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.AspNet.Mvc.TagHelpers": "6.0.0-rc1-final",
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.StaticFiles": "1.0.0-rc1-final",
"Microsoft.AspNet.Tooling.Razor": "1.0.0-rc1-final",
"Microsoft.Extensions.CodeGenerators.Mvc": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.FileProviderExtensions": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc1-final",
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-rc1-final",
"Microsoft.Extensions.Caching.Abstractions": "1.0.0-rc1-final",
"Microsoft.Extensions.Caching.Memory": "1.0.0-rc1-final"

In the above code, the highlighted references are for implementing caching. In ASP.NET Core 1.0, Caching is a service. This service needs to be added in our current application using Dependency Injection.

Step 3: In the project, add a new folder with class file in it, with following code in it

using System.Collections.Generic;

namespace InMemoryCaching.Models
{
    public class Employee
    {
        public int EmpNo { get; set; }
        public string EmpName { get; set; }
    }
    public class EmployeesDatabase : List<Employee>
    {
        public EmployeesDatabase()
        {
            Add(new Employee() { EmpNo = 1, EmpName = "A" });
            Add(new Employee() { EmpNo = 2, EmpName = "B" });
            Add(new Employee() { EmpNo = 3, EmpName = "C" });
            Add(new Employee() { EmpNo = 4, EmpName = "D" });
            Add(new Employee() { EmpNo = 5, EmpName = "E" });
            Add(new Employee() { EmpNo = 6, EmpName = "F" });
        }
    }

    public class DataAccess
    {
        public List<Employee> Get()
        {
            return new EmployeesDatabase();
        }
    }
}

The above code defines an Employee entity class and the EmployeeDatabase class with Employee data in it. The DataAccess class is used to access data.

Step 4: In the project, add a new folder of name Services with a class file. The class file will contain the following code:

using InMemoryCaching.Models;
using System.Collections.Generic;

namespace InMemoryCaching.Services
{
    public interface IService<T> where T :class
    {
        IEnumerable<T> Get();
    }

    public class EmployeeService : IService<Employee>
    {
        DataAccess ds;
        public EmployeeService(DataAccess d)
        {
            ds = d;
        }
        public IEnumerable<Employee> Get()
        {
            return ds.Get();
        }
    }
}

The above code contains the EmployeeService class implementing IService<T> interface. In the case T is Employee. The DataAccess object is passed to the EmployeeService using Constructor injection.

Step 4: In the project, we have Startup.cs file which contains Startup class. In the ConfigureServices() method, add the following code for using MVC, registering DataAccess and EmployeeService classes using Dependency Injection

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped(typeof(DataAccess));
    services.AddSingleton<IService<Employee>, EmployeeService>();
    services.AddCaching();
    services.AddMvc();
}

AddCaching() is an extension method which is responsible for adding Memory Caching service.

Step 5: In the Startup class, add the following code in Configure() method to use MVC routing and Diagnostics page

public void Configure(IApplicationBuilder app)
{
    app.UseIISPlatformHandler();

    app.UseDeveloperExceptionPage();

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Employee}/{action=Index}/{id?}");
    });
}

 

Step 6: In the project, add a new folder of name Controllers. In this folder, add a new MVC Controller class (Right Click > Add > New Item > MVC Controller Class). Name this class as EmployeeController. In this class, add the following code.

using InMemoryCaching.Models;
using InMemoryCaching.Services;
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;


namespace InMemoryCaching.Controllers
{
    public class EmployeeController : Controller
    {
        private readonly IMemoryCache _MemoryCache;

        private readonly IService<Employee> _service;
        //1
        public EmployeeController(IMemoryCache memCache,
            IService<Employee> serv)
        {
            _MemoryCache = memCache;
            _service = serv;
        }

        // GET: /<controller>/
        public IActionResult Index()
        {
            var Emps = SetGetMemoryCache();
            return View(Emps);
        }

        private List<Employee> SetGetMemoryCache()
        {
            //2
            string key = "MyMemoryKey-Cache";
            List<Employee> Employees;
    
            //3: We will try to get the Cache data
            //If the data is present in cache the 
            //Condition will be true else it is false 
            if (!_MemoryCache.TryGetValue(key, out Employees))
            {
                //4.fetch the data from the object
                Employees = _service.Get().ToList();
                //5.Save the received data in cache
                _MemoryCache.Set(key, Employees, 
                    new MemoryCacheEntryOptions()
                    .SetAbsoluteExpiration(TimeSpan.FromMinutes(1)));

               ViewBag.Status = "Data is added in Cache";

            }
            else
            {
                Employees = _MemoryCache.Get(key) as List<Employee>; 
                ViewBag.Status = "Data is Retrieved from in Cache";
            }
            return Employees;
        }
    }
}

In the above controller class, the private method SetGetMemoryCache() is used to store data in Memory Cache. This method does the following:

(Note: following line numbers match with comments applied on the above code.)

1. The Controller constructor is injected with MemoryCache interface and IService interface.

2. Using key, the cache data is read or written.

3. We will try to get the Cache data from the memory cache using TryGetValue() method with the first parameter as Cache Key and the second parameter as out parameter as Employees which is declared as the List<Employee>. If there is no data in cache, this method will return false else true.

4. This step fetches data using Get() method call from the service.

5. This step is responsible for saving the Employees data in cache using Set() method of MemoryCacheEntryOptions class. We need to define an absolute expiration for the cache. Currently we have set it to 1 Minute.

If the data is already present in cache, then else part of the code will be executed which will read data from Cache based on key and store it in Employees object. We are using ViewBag.Status to display Cache Message on the View.

We can also set the Sliding Expiration using SetSlidingExpiration() method of the MemoryCacheEntryOptions class to keep the data in class as it is requested, at least once in the stipulated time. We can do it using the following code:

new MemoryCacheEntryOptions()
        .SetSlidingExpiration(TimeSpan.FromMinutes(2))

E.g. In our case we have set it to 2 minutes.

 

This method is called in the Index() action method of the Controller class.

Step 7: In the project, add a new folder of name Views. In this folder, add a new MVC Imports View (Right Click > Add > New Item > MVC Imports View). Add the following code in it

@using InMemoryCaching;
@using InMemoryCaching.Models;
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"

This will be used to execute MVC View with new asp- attribute.

Step 8: In the Views folder, add a new subfolder of name Employee and in this folder, add a new MVC View of name Index.cshtml. Add the following markup code in it.

@model IEnumerable<InMemoryCaching.Models.Employee>

@{
     
    ViewData["Title"] = "Index";
}

<h2>Index</h2>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.EmpNo)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.EmpName)
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.EmpNo)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EmpName)
            </td>
        </tr>
    }
</table>

@{ }

The above code will show Employee Data.

Run the application, since this is the first run, the following result will be displayed

data-set-in-cache

The data is added in Cache. Now press F5, the result will be as following

asp.net-caching

The data is retrieved from cache.

Conclusion:

InMemory Cache in ASP.NET Core is one of the reliable ways to prevent an additional roundtrip to the data source for data that does not change frequently. This also improves performance of the application.

Download the entire source code of this article (Github)

  • Please Share this article if you think it was worth reading. Thanks!
Further Reading - Articles You May Like!
Author
Mahesh Sabnis is a Microsoft MVP having 14 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). Follow him on twitter @maheshdotnet


Page copy protected against web site content infringement by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!
comments powered by Disqus