Redis Cache in ASP.NET Core

This post describes how you can use Redis Cache as IDistributedCache in ASP.NET Core. Redis Cache is an in-memory data structure store, used as a database, cache and message broker. Redis Cache is used in multi-instance applications to handle session data and cached data.

As an alternative to Redis, you can use an sql database or a no-sql database to handle session data in a multi-instance application. Redis Cache has built-in replication, supports a lot of data structures, can provide high availability and it can be used on Azure.

Install Redis Server on Windows

You need to download and install Redis on your development machine to be able to test your implementation in ASP.NET Core. You can download an msi-file or a zip-file, the msi-file will install Redis and create a windows service that automatically starts Redis server on startup.

When you have installed Redis, make sure that the service has started and that you can ping the server. Open a command prompt as an administrator and browse to the folder where Redis is installed (cd “C:\Program Files\Redis”), start redis-cli.exe and type “ping”.

Application settings

We first need to install a NuGet-package (Microsoft.Extensions.Caching.Redis) to our project in Visual Studio, it is a distributed cache implementation of IDistributedCache using Redis. We store a connection string to Redis as application settings in secrets.json and appsettings.json. The default port for Redis is 6379, you might have used another port.

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Information"
    }
  },
  "RedisCacheOptions": {
    "ConnectionString": "localhost:6379"
  }
}

Services

We register services for memory cache, redis cache and sessions in the ConfigureServices method in the StartUp class. Memory cache is used as a backup if we do not want to use Redis. Our session service will use Redis Cache if it is registered or memory cache if we set the connection string to an empty string.

public void ConfigureServices(IServiceCollection services)
{
    // Add the mvc framework
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    // Create options
    services.Configure<CacheOptions>(options => { options.ExpirationInMinutes = 240d; });

    // Add memory cache
    services.AddDistributedMemoryCache();

    // Add redis distributed cache
    if (configuration.GetSection("RedisCacheOptions")["ConnectionString"] != "")
    {
        services.AddDistributedRedisCache(options =>
        {
            options.Configuration = configuration.GetSection("RedisCacheOptions")["ConnectionString"];
            options.InstanceName = "Mysite:";
        });
    }

    // Add the session service
    services.AddSession(options =>
    {
        // Set session options
        options.IdleTimeout = TimeSpan.FromMinutes(30d);
        options.Cookie.Name = ".Mysite";
        options.Cookie.Path = "/";
        options.Cookie.HttpOnly = true;
        options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
    });

} // End of the ConfigureServices method

We also have to use sessions in the Configure method in the StartUp class to automatically enable session state for the application.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // Use sessions
    app.UseSession();

} // End of the Configure method

Work with distributed cache

We have a class in which we have injected IDistributedCache, we can now use this interface to get data from redis cache and to save data to redis cache.

public class StaticTextRepository : IStaticTextRepository
{
    #region Variables

    private readonly ICosmosDatabaseRepository cosmos_database_repository;
    private readonly IDistributedCache distributed_cache;
    private readonly CacheOptions cache_options;
    private readonly ILanguageRepository language_repository;

    #endregion

    #region Constructors

    /// <summary>
    /// Create a new repository
    /// </summary>
    public StaticTextRepository(ICosmosDatabaseRepository cosmos_database_repository, IDistributedCache distributed_cache, IOptions<CacheOptions> cache_options, 
        ILanguageRepository language_repository)
    {
        // Set values for instance variables
        this.cosmos_database_repository = cosmos_database_repository;
        this.distributed_cache = distributed_cache;
        this.cache_options = cache_options.Value;
        this.language_repository = language_repository;

    } // End of the constructor

    #endregion

    #region Get methods

    public async Task<ModelItem<StaticTextsDocument>> GetByLanguageCode(string language_code)
    {
        // Create the sql string
        string sql = "SELECT VALUE s FROM s WHERE s.language_code = @language_code AND s.model_type = @model_type";

        // Create parameters
        SqlParameterCollection parameters = new SqlParameterCollection()
        {
            new SqlParameter("@language_code", language_code),
            new SqlParameter("@model_type", "static_text")
        };

        // Return the post
        return await this.cosmos_database_repository.GetByQuery<StaticTextsDocument>(sql, parameters);

    } // End of the GetByLanguageCode method

    public async Task<KeyStringList> GetFromCache(string language_code)
    {
        // Create the cacheId
        string cacheId = "StaticTexts_" + language_code.ToString();

        // Get the cached settings
        string data = this.distributed_cache.GetString(cacheId);

        // Create the list to return
        KeyStringList posts = new KeyStringList();

        if (data == null)
        {
            // Get the post
            ModelItem<StaticTextsDocument> static_texts_model = await GetByLanguageCode(language_code);

            // Make sure that something was found in the database
            if (static_texts_model.item == null)
            {
                return posts;
            }
            else
            {
                posts = new KeyStringList(static_texts_model.item.dictionary);
            }

            // Create cache options
            DistributedCacheEntryOptions cacheEntryOptions = new DistributedCacheEntryOptions();
            cacheEntryOptions.SetSlidingExpiration(TimeSpan.FromMinutes(this.cache_options.ExpirationInMinutes));
            cacheEntryOptions.SetAbsoluteExpiration(TimeSpan.FromMinutes(this.cache_options.ExpirationInMinutes));

            // Save data in cache
            this.distributed_cache.SetString(cacheId, JsonConvert.SerializeObject(posts), cacheEntryOptions);
        }
        else
        {
            posts = JsonConvert.DeserializeObject<KeyStringList>(data);
        }

        // Return the post
        return posts;

    } // End of the GetFromCache method

    #endregion

    #region Helper methods

    public async Task RemoveFromCache()
    {
        // Get all languages
        ModelItem<LanguagesDocument> languages_model = await this.language_repository.GetByType();

        // Loop languages
        foreach (KeyValuePair<string, string> entry in languages_model.item.dictionary)
        {
            // Get the data
            string data = this.distributed_cache.GetString("StaticTexts_" + entry.Key);

            // Only remove the cache if it exists
            if (data != null)
            {
                // Remove data from cache
                this.distributed_cache.Remove("StaticTexts_" + entry.Key);
            }
        }

    } // End of the RemoveFromCache method

    #endregion

} // End of the class

Work with sessions

We have added a session extension class to set and get serializable objects as session variables.

public static class SessionExtensions
{
    public static void Set<T>(this ISession session, string key, T value)
    {
        session.SetString(key, JsonConvert.SerializeObject(value));

    } // End of the Set method

    public static T Get<T>(this ISession session, string key)
    {
        var value = session.GetString(key);
        return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
    }

} // End of the class

Below are some examples on how you can set and get session variables in ASP.NET Core.

HttpContext.Session.SetString("SessionKey1", "VALUE 1");
HttpContext.Session.SetInt32("SessionKey2", 2);
string value1 = HttpContext.Session.GetString("SessionKey1");
Int32 value2 = HttpContext.Session.GetInt32("SessionKey2");
HttpContext.Session.Set<DateTime>("SessionKey3", DateTime.Now);
DateTime dt = HttpContext.Session.Get<DateTime>("SessionKey3");

Leave a Reply

Your email address will not be published. Required fields are marked *