Caching
Our caching provider is built on top of Microsoft.Extensions.Caching.Memory Caching is insanely easy with request handlers in Shiny Mediator.
-
Install Shiny.Mediator.Caching.MicrosoftMemoryCache
to your project.
-
Create your request handler - you can mark Cache here with the attribute or you can use Configuration.
[Cache(AbsoluteExpirationSeconds = 60, SlidingExpirationSeconds = 30)]public class MyHandler : IRequestHandler<MyRequest, MyResult>{public async Task<MyResult> Handle(MyRequest request, RequestContext<MyRequest> context, CancellationToken ct){return new MyResult();}} -
In your host startup, with the AddShinyMediator call, add the following:
services.AddShinyMediator(x => x..AddMemoryCaching(y =>{y.ExpirationScanFrequency = TimeSpan.FromSeconds(5);})); -
That’s it! Your handler will now cache for 60 seconds with a sliding expiration of 30 seconds.
Setting Cache Config on the Context
Sometimes, a very specific call from a very specific location may have a need to set the cache config from a call. You can do this through the IMediatorContext headers
IMediator mediator; // inject thisvar response = await mediator.Request(new MyRequest(), CancellationToken.None, ctx => ctx.SetCacheConfig(new CacheItemConfig{ AbsoluteExpiration = TimeSpan.FromSeconds(60), SlidingExpiration = TimeSpan.FromSeconds(30)}));
Forcing Cache Refresh
You can add the ICacheControl to your contract to control cache directly at the point of call.
ForceRefresh allows an easy way to bypass cache and SetEntry allows you to set cache entry properties.
IMediator mediator; // inject thisvar response = await mediator.Request(new MyRequest(), CancellationToken.None, ctx => ctx.ForceCacheRefresh());response.Result // won't be from cache
var cacheInfo = response.Context.Cache();cacheInfo.IsHit // will be false
Configuration
We recommend configuring cache through Microsoft.Extensions.Configuration. Read Configuration for more information.
{ "Mediator": { "Cache": { "My.Namespace.MyHandler": { "Priority": "High", "AbsoluteExpirationSeconds": 60, "SlidingExpirationSeconds": 30 } } }}
Persistent Cache
Persistent cache is built into our MAUI and Uno Platform extensions. It uses the file system to store cache data. This allows cache to survive across application restarts. It works identical to the memory cache, but you can specify a different cache provider.
services.AddShinyMediator(x =>{ x.AddMauiPersistentCache();
// OR x.AddUnoPersistentCache();});
It works identical to the memory cache, but stores to the filesystem instead. This is great for combining with things like Shiny Background Jobs to keep your cache fresh.
Custom Cache Providers
Custom caching is now easy to accomplish inside Shiny.Mediator. Your cache provider can also make use of all the same benefits/configuration as already shown.
-
Implement your custom provider
Shiny.Mediator.Caching.ICacheProvider
located in the Shiny.Mediator package.public interface ICacheService{Task<CacheEntry<T>?> GetOrCreate<T>(string key,Func<Task<T>> factory,CacheItemConfig? config = null);/// <summary>/// Manually insert or overwrite an item in cache/// </summary>/// <param name="key"></param>/// <param name="value"></param>/// <param name="config"></param>Task<CacaheEntry<T>> Set<T>(string key,T value,CacheItemConfig? config = null);/// <summary>/// Retrieves a cached value, null if not found/// </summary>/// <param name="key"></param>/// <typeparam name="T"></typeparam>/// <returns></returns>Task<CacheEntry<T>?> Get<T>(string key);/// <summary>/// Removes a specific cache item/// </summary>/// <param name="key"></param>Task Remove(string key);/// <summary>/// Clears cache keys starting with prefix/// </summary>/// <param name="prefix"></param>Task RemoveByPrefix(string prefix);/// <summary>/// Clears all cache/// </summary>Task Clear();} -
Now register it with Shiny.Mediator
services.AddShinyMediator(x => x.AddCaching<MyCustomCacheService>());You can only have 1 cache provider registered at a time for our middleware. If you need something more custom, you’ll need to create your own middleware to handle it.
Contexts & ‘Did my data come from cache’?
There is often times that you want to know IF your data came from cache, but also WHEN it was stored. This is easy to accomplish using
the IMediatorContext
interface. This interface is populated with tons of extra data during your mediator calls including offline data.
There is an extension method called IMediatorContext.Cache
- if this value is returned as null, the data is “fresh”, otherwise, it contains info about
when the data was stored.
IMediator mediator;var response = mediator.Request(new MyRequest());
response.Result // the actual datavar cache = response.Context.Cache();
if (!cache.IsHit){ // ... data is direct from source}else{ // data is from offline store cache.Timestamp // when the data was stored cache.RequestKey // the key used to store the data}