One ICaching interface for in-memory cache, Redis, and Memcached.
Sync and async, with dependency injection for .NET 8, 9, and 10.
Pick a provider once, then call the same methods everywhere. Switching from IMemoryCache to Redis or Memcached is a configuration change, not a rewrite of your services.
| Providers | In-memory, Redis, Memcached |
| API | Get-or-set, explicit get and set, invalidate one key or all, list keys |
| Styles | Synchronous and asynchronous |
| TTL | Seconds, minutes, hours, or days |
| Redis serialization | Newtonsoft.Json or protobuf-net |
dotnet add package EasyMemoryCacheOr from the Package Manager Console:
Install-Package EasyMemoryCacheRegister the cache, inject ICaching, and call get-or-set. The factory runs only on a miss.
using EasyMemoryCache.Configuration;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection()
.AddEasyCache(new CacheSettings
{
CacheProvider = CacheProvider.MemoryCache
});
var caching = services.BuildServiceProvider().GetRequiredService<ICaching>();
var users = await caching.GetOrSetObjectFromCacheAsync(
"users",
cacheTime: 20,
objectSettingFunction: LoadUsersAsync);builder.Services.AddEasyCache(new CacheSettings
{
CacheProvider = CacheProvider.MemoryCache
});public class UserService
{
private readonly ICaching _caching;
public UserService(ICaching caching) => _caching = caching;
public Task<List<User>> GetUsersAsync() =>
_caching.GetOrSetObjectFromCacheAsync("users", 20, LoadUsersAsync);
}A working API sample lives in EasyMemoryCache.SampleApi, and a console sample lives in src/EasyMemoryCache.Sample.
CacheProvider selects the backend. The default is in-memory.
builder.Services.AddEasyCache(new CacheSettings
{
CacheProvider = CacheProvider.MemoryCache
});"CacheSettings": {
"CacheProvider": "MemoryCache"
}builder.Services.AddEasyCache(new CacheSettings
{
CacheProvider = CacheProvider.Redis,
RedisConnectionString = "localhost:6379,abortConnect=False",
RedisSerialization = SerializationType.Protobuf
});RedisSerialization accepts Newtonsoft (the default) or Protobuf.
"CacheSettings": {
"CacheProvider": "Redis",
"RedisConnectionString": "localhost:6379,password=secret,ssl=False,abortConnect=False",
"RedisSerialization": "Protobuf"
}Pass the configuration section as the second argument so server and socket-pool settings are bound.
builder.Services.AddEasyCache(
builder.Configuration.GetSection("CacheSettings").Get<CacheSettings>(),
builder.Configuration.GetSection("CacheSettings"));"CacheSettings": {
"CacheProvider": "Memcached",
"Servers": [
{ "Address": "localhost", "Port": 11211 }
],
"SocketPool": {
"MinPoolSize": 5,
"MaxPoolSize": 25,
"ConnectionTimeout": "00:00:15",
"ReceiveTimeout": "00:00:15",
"DeadTimeout": "00:00:15",
"QueueTimeout": "00:00:00.150"
}
}The factory is skipped when the key is already cached.
var names = _caching.GetOrSetObjectFromCache("names", 20, LoadNames);
var namesAsync = await _caching.GetOrSetObjectFromCacheAsync("names", 20, LoadNamesAsync);Close over arguments when the factory needs them:
var names = await _caching.GetOrSetObjectFromCacheAsync(
"names",
20,
() => LoadNamesAsync(tenantId));Empty collections are not stored unless you opt in with cacheEmptyList: true. The default interval is minutes. Pass CacheTimeInterval to use seconds, hours, or days.
var snapshot = await _caching.GetOrSetObjectFromCacheAsync(
"snapshot",
cacheTime: 30,
objectSettingFunction: LoadSnapshotAsync,
cacheEmptyList: true,
interval: CacheTimeInterval.Seconds);_caching.SetValueToCache("token", token, cacheTime: 2, interval: CacheTimeInterval.Hours);
var token = _caching.GetValueFromCache<string>("token");
var tokenAsync = await _caching.GetValueFromCacheAsync<string>("token");_caching.Invalidate("users");
_caching.InvalidateAll();
await _caching.InvalidateAllAsync();foreach (var key in _caching.GetKeys())
{
Console.WriteLine(key);
}MIT © Thiago Loureiro