A simple tool to easily create caching proxies for types. To use, use the following steps:
- Register the caching manager to your services:
- Register methods you want to be proxied. This can be done declaratively, via attributes, or by implementing a custom
CacheHandler
. - Register an implementation of
IDistributedCache
- Wire up the cache proxy generator:
- Register any serializer instances you may need (must implement
ICacheSerializer<>
) which are specialized per method return type.
This library will automatically intercept and cache calls with the following return types:
Task<>
ValueTask<>
T
Note, that if the method is not async, the sync method on IDistributedCache
will be used instead so that no blocking occurs.
Logging is enabled in the app. If you want to see output, enable the logging filter:
services.AddLogging(builder =>
{
builder.AddFilter("Swick.Cache", LogLevel.Trace);
builder.AddDebug();
});
In order to hook into the proxy creation and injection, you can add custom CacheHandler
instances to CacheOptions.CacheHandlers
. There are some built in ones that can help with common tasks, such as setting default expiration, set up an attribute to identify methods for caching, etc.
public interface ITest
{
Task<object> ReturnObjectAsync();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCachingManager()
.Configure(options =>
{
options.CacheHandlers.Add(new DefaultExpirationCacheHandler(entry =>
{
entry.SlidingExpiration = TimeSpan.FromDays(5);
}));
})
.CacheType<ITest>(test => test.Add(nameof(ITest.ReturnObjectAsync)));
.AddAccessors();
}
public class UsageTest
{
public UsageTest(ICached<ITest> cached, ICacheInvalidator<ITest> invalidator)
{
cached.Instance.ReturnObjectAsync();
}
}
If you want to decorate services, use Autofac, Scrutor or some other system to help with that:
public interface ITest
{
Task<object> ReturnObjectAsync();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCachingManager()
.Configure(options =>
{
options.CacheHandlers.Add(new DefaultExpirationCacheHandler(entry =>
{
entry.SlidingExpiration = TimeSpan.FromDays(5);
}));
})
.CacheType<ITest>(test => test.Add(nameof(ITest.ReturnObjectAsync)));
.AddAccessors();
services.Decorate<ITest>((other, ctx) => ctx.GetRequiredService<ICachingManager>().CreateCachedProxy(other));
}
public class UsageTest
{
public UsageTest(ITest cached)
{
cached.ReturnObjectAsync();
}
}
You can register an attribute to define the methods to cache as well as identify the expiration policy:
[AttributeUsage(AttributeTargets.Method)]
public sealed class CachedAttribute : Attribute
{
private readonly int _days;
public CachedAttribute(int days)
{
_days = days;
}
public TimeSpan SlidingExpiration => TimeSpan.FromDays(_days);
}
public interface ITest
{
[Cached(5)]
Task<object> ReturnObjectAsync();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCachingManager()
.Configure(options =>
{
options.CacheHandlers.Add(new AttributeCacheHandler<CachedAttribute>((a, entry) =>
{
entry.SlidingExpiration = a.SlidingExpiration;
}));
});
services.Decorate<ITest>((other, ctx) => ctx.GetRequiredService<ICachingManager>().CreateCachedProxy(other));
}
public class UsageTest
{
public UsageTest(ITest cached)
{
cached.ReturnObjectAsync();
}
}
In order to build/test this project, you will want to ensure you're using the most up-to-date .NET toolchain (.NET CLI, VS, etc).