Elevating Performance: In-Memory Caching in ASP.NET Core

Firat Tonak
5 min readDec 13, 2022

--

As you may be aware, caching serves as a dual-purpose tool, enhancing performance while curbing excessive database connections. The choice often boils down to in-memory or distributed cache.

In the realm of monolithic architecture projects, the in-memory cache takes center stage. Its simplicity and effectiveness make it the go-to solution for bolstering application performance.

Embark on this journey by initiating the creation of a web application. Navigate to Visual Studio (assuming it’s your preferred IDE) and initiate the creation of a Web API app.

Select “ASP.NET Core Web API” and propel yourself forward by clicking on the Next button.

Provide the necessary details and proceed by clicking the Next button.

Maintain the current settings and proceed by clicking the Create button.

After creating the project, the next step is to install the Microsoft.Extensions.Caching.Memory package. Right-click on your solution and choose “Manage NuGet Packages.”

Search for “Microsoft.Extensions.Caching.Memory” and click on the Install button to initiate the installation process.

After installing the package, navigate to the program.cs file and incorporate AddMemoryCache() to integrate the memory cache into your application.

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddMemoryCache();

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Navigate to the controller and enhance it with a caching mechanism. Inject IMemoryCache into the controller for seamless integration.

private readonly IMemoryCache _cache; 

public InMemoryTestController(IMemoryCache cache)
{
this._cache = cache;
}

Implement a HttpGet method and incorporate a caching mechanism within it.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;

namespace InMemoryCaching.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class InMemoryTestController : ControllerBase
{
private readonly IMemoryCache _cache;

public InMemoryTestController(IMemoryCache cache)
{
this._cache = cache;
}

[HttpGet]
public IActionResult InMemoryCache()
{
string result = "";
var cacheResult = _cache.Get<string>("CacheKey") ?? "";
if (cacheResult=="")
{
result = FakeDbConnection();
// you can put everything as a cachekey.
// you can configure cache time up to your requirements.
_cache.Set<string>("CacheKey", result, TimeSpan.FromMinutes(2));
}
return Ok(result);
}


private string FakeDbConnection()
{
return "Connected Db";
}

}
}

Upon initial execution, the method will invoke the FakeDbConnection method since the cache is empty. On subsequent attempts, you'll observe that the cache promptly returns the stored value, and the code efficiently proceeds to return Ok(result) directly.

Additionally, the TryGetValue method is employed to verify the presence of the value in the cache. If the value is found, the method promptly returns the cached value; otherwise, it connects to the FakeDbConnection method.

 if (!_cache.TryGetValue("CacheKey",out result))
{
result = FakeDbConnection();
_cache.Set<string>("CacheKey", result, TimeSpan.FromMinutes(2));
}
return Ok(result);

InMemoryTestController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;

namespace InMemoryCaching.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class InMemoryTestController : ControllerBase
{
private readonly IMemoryCache _cache;

public InMemoryTestController(IMemoryCache cache)
{
this._cache = cache;
}

[HttpGet]
public IActionResult InMemoryCache()
{
string result = "";
//var cacheResult = _cache.Get<string>("CacheKey") ?? "";
//if (cacheResult=="")
//{
// result = FakeDbConnection();
// // you can put everything as a cachekey.
// // you can configure cache time up to your requirements.
// _cache.Set<string>("CacheKey", result, TimeSpan.FromMinutes(2));
//}

if (!_cache.TryGetValue("CacheKey",out result))
{
result = FakeDbConnection();
_cache.Set<string>("CacheKey", result, TimeSpan.FromMinutes(2));
}
return Ok(result);
}


private string FakeDbConnection()
{
return "Connected Db";
}

}
}

This delineates the operational dynamics of the caching mechanism, a pivotal element in optimizing performance. For enhanced control, consider predefining cache entry options before assigning values, thereby augmenting the sophistication of your caching strategy.

InMemoryTestController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;

namespace InMemoryCaching.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class InMemoryTestController : ControllerBase
{
private readonly IMemoryCache _cache;

public InMemoryTestController(IMemoryCache cache)
{
this._cache = cache;
}

[HttpGet]
public IActionResult InMemoryCache()
{
string result = "";
//var cacheResult = _cache.Get<string>("CacheKey") ?? "";
//if (cacheResult=="")
//{
// result = FakeDbConnection();
// // you can put everything as a cachekey.
// // you can configure cache time up to your requirements.
// _cache.Set<string>("CacheKey", result, TimeSpan.FromMinutes(2));
//}

//if (!_cache.TryGetValue("CacheKey", out result))
//{
// result = FakeDbConnection();
// _cache.Set<string>("CacheKey", result, TimeSpan.FromMinutes(2));
//}

if (!_cache.TryGetValue("CacheKey", out result))
{
result = FakeDbConnection();
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromHours(1))//how long it will be inactive
.SetAbsoluteExpiration(TimeSpan.FromMinutes(10))// if there is a problem with sliding expiration, the absolute expiration will determine the cache
.SetPriority(CacheItemPriority.High)// how important the cache is
.SetSize(2048);//size of cache entry value
_cache.Set<string>("CacheKey", result, cacheOptions);
}


return Ok(result);
}

private string FakeDbConnection()
{
return "Connected Db";
}

}
}

Primarily, the process involves sending the key to retrieve the associated value when utilizing the cache. It’s essential to bear in mind that the cache accommodates not only string values but also variables, models, and various data types.

For those considering distributed caching, the same underlying logic applies. However, it necessitates the installation of Docker on your machine and the creation of a container for the caching mechanism. A more in-depth exploration of the distributed caching mechanism will be undertaken in a subsequent article.

To delve into the code, you can access it here.

--

--

Firat Tonak

Seasoned Senior Full-Stack .NET Developer sharing hands-on experience and cutting-edge technologies in comprehensive Full-Stack development. #TechInsights