Microsoft Workloads on AWS

Using AWS Services for distributed caching in ASP.NET Core Web Applications

Software development engineers invest thought and effort into optimizing the performance of the applications they build, often using strategies such as caching frequently used data, moving code closer to the users, optimizing code, optimizing data access and disk I/O, and response compression.

In this blog post, we explore one such optimization – caching in ASP.NET Core web applications – and discuss two Amazon Web Services (AWS) services you can use to implement that caching: Amazon Relational Database Service (Amazon RDS) for SQL Server and Amazon ElastiCache for Redis.

Solution Overview

Caching works by storing data that is accessed frequently but changes infrequently – e.g., static data from an API – in a high-read, performance data store known as the cache. ASP.NET Core provides several ways to incorporate caching into your applications.

The recommended approach to cache data in a web server farm for ASP.NET Core web applications is to use distributed caching. A distributed cache is maintained as an external service separate from your application servers. It enables independent scaling of your application and cache environments, improves fault tolerance, and ensures availability of cached data across deployments and server restarts in load-balanced environments.

Distributed caches in ASP.NET Core implement the IDistributedCache interface. Two implementations are provided with .NET Core: the SQL Server Distributed Cache and the Redis Distributed Cache. In the following sections, we will step through implementing a SQL Server Distributed Cache backed by an Amazon RDS for SQL Server database and a Redis Distributed Cache backed by Amazon ElastiCache for Redis.

You can also create your own IDistributedCache implementation to meet your specific requirements.. For example, if you want to use Amazon Aurora as the backing store for your distributed cache, you could create your own PostgreSQL or MySQL implementation of the IDistributedCache interface.

For applications that are hosted on a single server, you could also use the ASP.NET Core IMemoryCache implementation, which simply caches data in the memory of the web server.

Prerequisites

  1. Download and install the .NET 6.0 SDK.
  2. Download and install SQL Server Management Studio.
  3. Create an RDS for SQL Server DB instance. Note that if you want to connect to this database from an environment outside your Amazon Virtual Private Cloud (Amazon VPC), you will need to configure the database to be publicly accessible.
  1. Create an ElastiCache for Redis Cluster. Here are a few things to keep in mind before you create your cluster:
    1. Based on the Redis version you want to use, you have the option of creating a ElastiCache for Redis cluster with cluster mode enabled or disabled. Refer to the service documentation while making that decision, keeping in mind your current and future needs.
    2. Depending on your specific scenario, you’ll use one of these access patterns to access your ElastiCache Cluster.

In the following command, replace [bucket-name] with the name of the Amazon Simple Storage Service (S3) bucket you want to use for snapshots:

aws elasticache create-cache-cluster ^
--cache-cluster-id <name-of-the-elasticache-cluster> ^
--cache-node-type cache.r4.large ^
--engine redis ^
--engine-version 6.2 ^
--num-cache-nodes 1 ^
--cache-parameter-group default.redis6.x ^
--snapshot-arns arn:aws:s3:::[bucket-name]/snapshot.rdb

Amazon RDS for SQL Server

Amazon RDS for SQL Server is a managed service that makes it simple to set up, operate, and scale Microsoft SQL Server databases in the cloud. Amazon RDS takes care of the undifferentiated heavy lifting that is typically associated with relational database management, such as backups, security patching, and OS updates. In this section, we will create and configure an Amazon RDS for SQL Server database as a distributed cache for an ASP.NET web application.

  1. Use SQL Server Management Studio to connect to the Amazon RDS for SQL Server database you created in the prerequisites section.
  2. Create a new database called Cache.
  3. Open a terminal on your local development machine and install the sql-cache command by executing the following command:dotnet tool install --global dotnet-sql-cache
  4. Use the following command to create the caching schema, replacing [ConnectionString] with the Cache database’s connection string:dotnet sql-cache create “[ConnectionString]“ dbo CacheData
  5. Create an empty folder – for example, C:\DemoApp — and navigate to that folder.
  6. Use the following command to create a new ASP.NET Core web application: dotnet new webapp -n SqlServerDistributedCacheDemo
  7. Use the following command to add the Extensions.Caching.SqlServer NuGet package to your ASP.NET Core application. dotnet add package Microsoft.Extensions.Caching.SqlServer
  8. Once the package is installed, you need to bootstrap your ASP.NET Core web application with the SQL Server distributed cache. Open Program.cs in your preferred code editor and replace the entire contents with the following code (replacing [Server] and [Password] in the connection string with appropriate values):
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDistributedSqlServerCache(x =>
{
    x.ConnectionString = "Data Source=[Server];Initial Catalog=Cache;User ID=admin;Password=[Password];";
    x.SchemaName = "dbo";
    x.TableName = "CacheData";
});

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

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();

Important Note: Although the code above suggests hardcoding your connection string within your application, this is for demonstration purposes only. In practice, you should never hardcode sensitive information like passwords and connection strings in your application. Instead, use services like AWS Secrets Manager or Parameter Store to store sensitive data and retrieve it when required.

Your application is now configured to cache data in your Amazon RDS for SQL Server database. Let’s test this by writing some data to the cache when the application home page is loaded.

  1. Open Index.cshtml.cs in your preferred code editor and replace the entire contents with the following code. Here, we are injecting an instance of IDistributedCache into the page model then storing a new cache item with a key of CacheTime and a value that is equal to the current UTC DateTime.
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    using Microsoft.Extensions.Caching.Distributed;
    
    namespace SqlServerDistributedCacheDemo.Pages
    {
        public class IndexModel : PageModel
        {
            private readonly ILogger<IndexModel> _logger;
            private readonly IDistributedCache _cache;
    
            public IndexModel(ILogger<IndexModel> logger, IDistributedCache cache)
            {
                _logger = logger;
                _cache = cache;
            }
    
            public async Task OnGet()
            {
                await _cache.SetStringAsync("CacheTime", DateTime.UtcNow.ToString());
            }
        }
    }
    
  2. Run the application. Once the home page has loaded in a browser, open SQL Server Management Studio and connect to your Amazon RDS for SQL Server database instance. Open the Cache database and query the data in the CacheData table. If it is configured correctly, your cached data will be stored in the table.
CacheTime entry in SQL Server

Figure 1: CacheTime entry in SQL Server

If you already have a SQL Server database as part of your application, using that SQL Server database for your distributed cache is an effective way to reduce the cost of your solution. If you are heavily focused on cost optimization, then this may be a better option than creating a Redis cluster (see below) or using a dedicated SQL Server database for your distributed cache.

Amazon Elasticache for Redis

Amazon Elasticache for Redis is a distributed in-memory data store that is built on open-source Redis. It works with Redis APIs, standard Redis client libraries, and uses open Redis data formats. Amazon ElastiCache for Redis can be used as a cache environment for your cloud applications in AWS. The service is simple to set up, manage, and scale. It is an AWS managed service with no servers for customers to manage, thereby reducing operational overhead.

Amazon ElastiCache cluster instances are meant to be accessed via compute located within a VPC. Note that you will have to deploy your application to AWS to consume the ElastiCache cluster endpoint. External access for test or development purposes can be achieved, but discouraged due to concerns related to network latency and security. You can learn more about that, as well as steps required for granting access to your cluster from EC2 instances, from the Amazon ElastiCache for Redis user guide.

In this section, we will set up and configure Amazon ElastiCache for Redis as a distributed cache provider for your ASP.NET Core web applications.

  1. Once you have created your Amazon ElastiCache for Redis cluster, you can use one of these options to view the cluster’s details. Make a note of the primary endpoint of your Redis cluster. You’ll need this to configure your ASP.NET Core web application to connect to.

ElastiCache for Redis Cluster details

Figure 2: ElastiCache for Redis Cluster details

  1. Next, let’s create and configure your ASP.NET Core web application to use the Redis cluster to serve as a distributed caching layer for your application. The distributed cache will be used as the data store to save the application’s cache data.
    1. Create a new ASP.NET Core web application by running the following .NET CLI command: dotnet new webapp -n RedisDistributedCacheDemo
    2. Add the Extensions.Caching.StackExchangeRedis NuGet package to your ASP.NET Core web application by running dotnet add package microsoft.Extensions.Caching.StackExchangeRedis. It provides an implementation of IDistributedCache for Redis.
    3. Now, let’s bootstrap the application with Redis Cache. Open Program.cs in your preferred code editor and replace the entire contents with the following code. Replace [primary-endpoint-of-your-elasticache-cluster] with the Primary endpoint of the ElastiCache for Redis cluster from the earlier step.
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = “[primary-endpoint-of-your-elasticache-cluster]”;
    options.InstanceName = "ElastiCacheExample";
});


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

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();

As pointed out in the Amazon RDS for SQL Server section, you should store your configuration values like the cluster endpoints in AWS Secrets Manager or Parameter Store, a capability of AWS Systems Manager, and avoid hardcoding them within your application.

Your application is now configured to use Amazon ElastiCache for Redis as a distributed cache.

To test, open Index.cshtml.cs and replace the existing code with the code below:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Caching.Distributed;

namespace RedisDistributedCacheDemo.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;
        private readonly IDistributedCache _cache;

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

        public async Task OnGet()
        {
            await _cache.SetStringAsync("CacheTime", DateTime.UtcNow.ToString());
        }
    }
}

Note that the way to use this in your application is the same way as described in the Amazon RDS for SQL Server section. You have just configured the application to use an implementation of IDistributedCache that uses Amazon ElastiCache for Redis.

As an in-memory data store, Amazon ElastiCache for Redis provides a high performance cache for your application. It provides added benefits if used when the application that is accessing the cache is either hosted in the same Amazon VPC as the Redis cluster or an Amazon VPC that is peered to it, due to lower network latency.

Cleanup

When you are done testing, delete the resources that you created from this blog post to avoid incurring any unnecessary charges. The instructions in the Amazon RDS user guide will walk you through the steps needed to delete your Amazon RDS instance. The Amazon Elasticache for Redis user guide has instructions for deleting the ElastiCache cluster.

Pricing

For pricing information, refer the service pricing documentation below. Both services have eligibility to be used with the AWS Free Tier.

Summary

Adding a distributed cache is one way to improve the performance and scalability of your ASP.NET Core web applications. By offloading the cache from the local memory of your app servers, you help your application scale out more effectively.

If you want to use an IDistributedCache implementation to save your session state data, refer to the Microsoft documentation on session and state management in ASP.NET Core.

AWS provides several managed services that can serve as a managed cache for your ASP.NET Core web applications. We examined how Amazon RDS for SQL Server and Amazon ElastiCache for Redis can be used as a distributed cache layer for your applications. They are managed services that are simple to set up and scale, with low operational overhead.

Visit the .NET on AWS page to learn about building, deploying, and developing .NET applications on AWS. You can also follow the blog channel Microsoft Workloads on AWS for blog posts that cover technical how-tos, solution architectures, and customer case studies in an easy-to-consume format. The .NET on AWS YouTube playlist also has a variety of demos on services you can use with your .NET development.


AWS can help you assess how your company can get the most out of cloud. Join the millions of AWS customers that trust us to migrate and modernize their most important applications in the cloud. To learn more on modernizing Windows Server or SQL Server, visit Windows on AWSContact us to start your modernization journey today.

Jagadeesh Chitikesi

Jagadeesh Chitikesi

Jagadeesh is a Sr. Microsoft Specialist Solutions Architect at AWS. He worked as a developer and an architect with enterprises of all sizes across healthcare, finance, retail, utility, and government for the last 20 years. He is passionate about cloud and all the innovation happening at AWS.

Brad Webber

Brad Webber

Brad is a Senior Microsoft Solutions Architect at Amazon Web Services (AWS). He started writing code when Visual Basic 6 was still a thing. These days he spends most of his time helping organizations modernize their .NET workloads and migrate them to the cloud.