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
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
- https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.caching.distributed.idistributedcache?view=aspnetcore-2.0
- https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-2.0
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.
Comments for this article