Simple and powerful strongly typed read-through caching extensions for .NET's IDistributedCache
Capper provides extension methods for IDistributedCache which encapsulate the business logic (and drastically simplify the implementation) of read-through caching.
It is partly inspired by the way Dapper provides light, unopinionated strongly typed ORM extensions to SqlConnection (i.e. Capper is Dapper for caching).
Because it utilises IDistributedCache, Capper supports either in-memory (default) or durable caches.
The library is in general availability and is available as a NuGet package.
Adding read through caching to your .NET application has never been easier.
Install the NuGet package with a package manager.
private readonly AnimalRepository repository;
public AnimalController(AnimalRepository repository)
{
this.repository = repository;
}
[HttpGet("{id}")]
public async Task<Animal> Get(string id)
{
return await repository.GetAsync(id);
}
private readonly AnimalRepository repository;
private readonly IDistributedCache cache;
public AnimalCacheController(AnimalRepository repository, IDistributedCache cache)
{
this.repository = repository;
this.cache = cache;
}
[HttpGet("{id}")]
public async Task<Animal> Get(string id)
{
return await cache.ReadThroughAsync(id,
async () => await repository.GetAsync(id));
}
public void ConfigureServices(IServiceCollection services)
{
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "127.0.0.1:6379";
options.InstanceName = "Animals";
});
services.AddRazorPages();
}
public class JsonCacheSerializer : ICacheSerializer
{
public T Deserialize<T>(byte[] serialized)
{
return JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(serialized));
}
public byte[] Serialize<T>(T deserialized)
{
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(deserialized));
}
}
public async Task<Animal> Get(string id)
{
return await cache.ReadThroughAsync(id,
async () => await repository.GetAsync(id),
new JsonCacheSerializer());
}
[HttpGet("{id}")]
public async Task<Animal> Get(string id)
{
return await cache.ReadThroughAsync(animalId, async () =>
await connection.QuerySingleAsync<Animal>($"SELECT TOP 1 FROM Animals WHERE Id = @AnimalId", new { AnimalId = animalId })
);
}
public interface IAnimalCache : IDistributedCache {}
public class AnimalCache : MemoryDistributedCache, IAnimalCache
{
public AnimalCache(IOptions<MemoryDistributedCacheOptions> optionsAccessor) : base(optionsAccessor)
{
}
}
public interface IVehicleCache : IDistributedCache {}
public class VehicleCache : RedisCache, IVehicleCache
{
public VehicleCache(IOptions<RedisCacheOptions> optionsAccessor) : base(optionsAccessor)
{
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAnimalCache, AnimalCache>();
services.AddSingleton<IVehicleCache, VehicleCache>();
}