ASP.NET Core 8 Web API -

Caching

Caching is a critical technique to improve the performance and scalability of your web applications by reducing the load on your server and speeding up response times for your users. ASP.NET Core 8 provides various ways to implement caching. This guide covers how to implement caching in an ASP.NET Core 8 Web API, along with best practices and detailed explanations.


1. Introduction to Caching

Caching is the process of storing data in a temporary storage location to reduce the time and resources needed to access it. It helps to improve application performance by reducing the number of requests to the server and speeding up response times.


2. Types of Caching

ASP.NET Core supports several types of caching:


3. Implementing In-Memory Caching

In-memory caching stores data in the memory of the web server. It's fast and simple to implement but is not suitable for distributed environments.

3.1 Configure Services Program.cs:
        
            var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddMemoryCache();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
        
    


3.2 Using In-Memory Cache in a Controller Example:
        
            [ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IMemoryCache _cache;
    private readonly ILogger<ProductsController> _logger;

    public ProductsController(IMemoryCache cache, ILogger<ProductsController> logger)
    {
        _cache = cache;
        _logger = logger;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetProduct(int id)
    {
        var cacheKey = $"Product_{id}";

        if (!_cache.TryGetValue(cacheKey, out Product product))
        {
            product = await GetProductFromDatabaseAsync(id); // Assume this is a method to fetch the product from the database

            var cacheEntryOptions = new MemoryCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
                SlidingExpiration = TimeSpan.FromMinutes(2)
            };

            _cache.Set(cacheKey, product, cacheEntryOptions);
        }

        return Ok(product);
    }

    private Task<Product> GetProductFromDatabaseAsync(int id)
    {
        // Simulate a database call
        return Task.FromResult(new Product { Id = id, Name = "Product " + id });
    }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}
        
    

4. Implementing Distributed Caching

Distributed caching stores data in an external cache such as Redis or SQL Server. It's suitable for web farm environments where multiple instances of the application need to share the same cached data.

4.1 Configure Redis Distributed Cache Program.cs:
        
            var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "SampleInstance";
});

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
        
    


4.2 Using Distributed Cache in a Controller Example:
        
            [ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IDistributedCache _cache;
    private readonly ILogger<ProductsController> _logger;

    public ProductsController(IDistributedCache cache, ILogger<ProductsController> logger)
    {
        _cache = cache;
        _logger = logger;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetProduct(int id)
    {
        var cacheKey = $"Product_{id}";
        var cachedProduct = await _cache.GetStringAsync(cacheKey);

        if (string.IsNullOrEmpty(cachedProduct))
        {
            var product = await GetProductFromDatabaseAsync(id); // Assume this is a method to fetch the product from the database
            var productJson = JsonSerializer.Serialize(product);

            var cacheEntryOptions = new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
                SlidingExpiration = TimeSpan.FromMinutes(2)
            };

            await _cache.SetStringAsync(cacheKey, productJson, cacheEntryOptions);
            return Ok(product);
        }

        var cachedProductObj = JsonSerializer.Deserialize<Product>(cachedProduct);
        return Ok(cachedProductObj);
    }

    private Task<Product> GetProductFromDatabaseAsync(int id)
    {
        // Simulate a database call
        return Task.FromResult(new Product { Id = id, Name = "Product " + id });
    }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}
        
    

5. Implementing Response Caching

Response caching reduces the need to generate the same HTTP response multiple times. It stores the responses and serves them directly from the cache for subsequent requests.

5.1 Configure Response Caching Middleware Program.cs:
        
            var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseResponseCaching();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
        
    


5.2 Applying Response Caching to Controllers Example:
        
            [ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}")]
    [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
    public IActionResult GetProduct(int id)
    {
        var product = new Product { Id = id, Name = "Product " + id };
        return Ok(product);
    }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}
        
    

6. Distributed Caching With Redis

Redis is a popular in-memory data store that can be used as a distributed cache. It is suitable for applications deployed in a web farm or cloud environment where multiple instances of the application need to share cached data.

6.1 Configure Redis Cache Program.cs:
        
            var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddControllers();
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("Redis");
    options.InstanceName = "SampleInstance";
});

var app = builder.Build();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
        
    


6.2 Using Redis Cache in a Controller Example:
        
            [ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IDistributedCache _cache;
    private readonly ILogger<ProductsController> _logger;

    public ProductsController(IDistributedCache cache, ILogger<ProductsController> logger)
    {
        _cache = cache;
        _logger = logger;
    }

    [HttpGet("{id}")]
    public async Task<IActionResult> GetProduct(int id)
    {
        var cacheKey = $"Product_{id}";
        var cachedProduct = await _cache.GetStringAsync(cacheKey);

        if (string.IsNullOrEmpty(cachedProduct))
        {
            var product = await GetProductFromDatabaseAsync(id); // Assume this is a method to fetch the product from the database
            var productJson = JsonSerializer.Serialize(product);

            var cacheEntryOptions = new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
                SlidingExpiration = TimeSpan.FromMinutes(2)
            };

            await _cache.SetStringAsync(cacheKey, productJson, cacheEntryOptions);
            return Ok(product);
        }

        var cachedProductObj = JsonSerializer.Deserialize<Product>(cachedProduct);
        return Ok(cachedProductObj);
    }

    private Task<Product> GetProductFromDatabaseAsync(int id)
    {
        // Simulate a database call
        return Task.FromResult(new Product { Id = id, Name = "Product " + id });
    }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}
        
    

6.3 Best Practices for Using Redis

7. Best Practices for Caching


8. Conclusion

Caching is an effective way to improve the performance and scalability of your ASP.NET Core 8 Web API. By using in-memory caching for simple scenarios, distributed caching for scalable environments, and response caching for repeated HTTP responses, you can significantly reduce server load and improve response times. Follow best practices to ensure that your caching strategy is effective and maintains data integrity.