Using strongly typed objects with IDistributedCache instead of byte arrays

An easy way to cache any object to distributed cache store using .NET Core

Saving an object to distributed cache store in .NET Core requires your object to be serialized to byte array in order for SetAsync method on Microsoft.Extensions.Caching.Distributed.IDistributedCache interface implemented instance to save your object to the injected cache store instance.

This means that what ever instance you have, you need to serialize it first to byte array. Similar, just reversed process applies for the process of reading an object from the distributed cache store.

That is not that difficult, but considering that you might do that in multiple places makes sense to have it as a separate method. To follow .NET Core stile, which I relly like and used to apply it even before .NET Core was released is using Extension methods, so I wrote two extension methods for this purpose

Note

For this purpose I used BinaryFormatter for binary serialization, but you can use any other serialization format. More on serialization formats you can find in this article http://bit.ly/2JsCjQH

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace DynamicModel.Core.Common.Extensions
{
    public static class Serialization
    {
        public static byte[] ToByteArray(this object obj)
        {
            if (obj == null)
            {
                return null;
            }
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            using (MemoryStream memoryStream = new MemoryStream())
            {
                binaryFormatter.Serialize(memoryStream, obj);
                return memoryStream.ToArray();
            }
        }

        public static T FromByteArray<T>(this byte[] byteArray) where T : class
        {
            if (byteArray == null)
            {
                return default(T);
            }
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            using (MemoryStream memoryStream = new MemoryStream(byteArray))
            {
                return binaryFormatter.Deserialize(memoryStream) as T;
            }
        }

    }
}
    

This way I avoid code repetition and I can call ToByteArray method on any class instance as long as I reference the extension method class namespace in the class where I want to use this extension method. Same for reading values, I can always call FromByteArray<T> on any byte[] instance and get the value as an instance of T directly from the method.

Now this is fine and makes things a lot easier and more convenient, but I went one step further and wrote two more extensions which can be used instead of SetAsync and GetAsync which rely on previous two extension methods.

using Microsoft.Extensions.Caching.Distributed;
using System.Threading;
using System.Threading.Tasks;

namespace DynamicModel.Core.Common.Extensions
{
    public static class DistributedCaching
    {
        public async static Task SetAsync<T>(this IDistributedCache distributedCache, string key, T value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
        {
            await distributedCache.SetAsync(key, value.ToByteArray(), options, token);
        }

        public async static Task<T> GetAsync<T>(this IDistributedCache distributedCache, string key, CancellationToken token = default(CancellationToken)) where T : class
        {
            var result = await distributedCache.GetAsync(key, token);
            return result.FromByteArray<T>();
        }
    }
}

    

Since these two methods are extensions on IDistributedCache interface they can be used straight forward in the code where IDistributedCache injected instance is available like following

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
using DynamicModel.Core.Common.Extensions;
namespace DynamicModel.Core.Web.Api.Controllers
{
public class SampleModel
{
public Guid Id { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public DateTime RegistrationDate { get; set; }
}

    [Produces("application/json")]
    [Route("api/Sample")]
    public class SampleController : Controller
    {
        private readonly IDistributedCache distributedCache;

        public SampleController(IDistributedCache distributedCache)
        {
            this.distributedCache = distributedCache;
        }

        [HttpGet]
        public async Task<IEnumerable<SampleModel>> Get()
        {
            var input = new List<SampleModel>() {
                new SampleModel(){Id=Guid.NewGuid(), FirstName="Bruce", LastName="Wane"},
                new SampleModel(){Id=Guid.NewGuid(), FirstName="Peter", LastName="Parker"}
            };
            await distributedCache.SetAsync<IEnumerable<SampleModel>>("SuperHeroesKey", input, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(2) });
            var output = await distributedCache.GetAsync<IEnumerable<SampleModel>>("SuperHeroesKey");

            return output;
        }
    }
}
    

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform includion ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article