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]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);}));
There are many additional properties you can use to interact with the cache setup
public class CacheAttribute : Attribute{ public int AbsoluteExpirationSeconds { get; set; } public int SlidingExpirationSeconds { get; set; }}
Deeper Cache Control
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.
public class MyContract : IRequest<SomeResponse>, ICacheControl{ public bool ForceRefresh { get; set; } public Action<ICacheEntry>? SetEntry { get; set; } // optional: allows you to set cache entry properties}
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 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 == null){ // ... 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}