Created
December 6, 2023 08:46
-
-
Save matthewrosse/d9c052ed1b3631ba57a9589fb0b01e64 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using DomainDrivenDesignExample.Api.Features.Common.Caching; | |
using DomainDrivenDesignExample.Api.Features.Common.Messaging; | |
using MediatR; | |
namespace DomainDrivenDesignExample.Api.Features.Common.Behaviors; | |
internal sealed class CacheQueryBehavior<TQuery, TQueryResponse> | |
( | |
ICacheService cacheService | |
) : IPipelineBehavior<TQuery, TQueryResponse> | |
where TQuery : ICachableQuery<TQueryResponse> | |
where TQueryResponse : class | |
{ | |
public async Task<TQueryResponse> Handle( | |
TQuery request, | |
RequestHandlerDelegate<TQueryResponse> next, | |
CancellationToken cancellationToken | |
) => await cacheService.GetAsync( | |
request.QueryKey, | |
async () => await next(), | |
cancellationToken); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Concurrent; | |
using System.Text.Json; | |
using Microsoft.Extensions.Caching.Distributed; | |
namespace DomainDrivenDesignExample.Api.Features.Common.Caching; | |
internal sealed class CacheService | |
(IDistributedCache distributedCache) : ICacheService | |
{ | |
private static readonly ConcurrentDictionary<string, bool> CacheKeys = new(); | |
public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default) | |
where T : class | |
{ | |
var cachedValue = await distributedCache.GetStringAsync(key, cancellationToken); | |
if (cachedValue is null) | |
{ | |
return null; | |
} | |
var value = JsonSerializer.Deserialize<T>(cachedValue); | |
return value; | |
} | |
public async Task<T> GetAsync<T>(string key, Func<Task<T>> valueFactory, | |
CancellationToken cancellationToken = default) where T : class | |
{ | |
var cachedValue = await GetAsync<T>(key, cancellationToken); | |
if (cachedValue is not null) | |
{ | |
return cachedValue; | |
} | |
var newCachedValue = await valueFactory(); | |
await SetAsync(key, newCachedValue, cancellationToken); | |
return newCachedValue; | |
} | |
public async Task SetAsync<T>(string key, T value, CancellationToken cancellationToken = default) where T : class | |
{ | |
var cachedValue = JsonSerializer.Serialize(value); | |
await distributedCache.SetStringAsync(key, cachedValue, cancellationToken); | |
CacheKeys.TryAdd(key, true); | |
} | |
public async Task RemoveAsync(string key, CancellationToken cancellationToken = default) | |
{ | |
await distributedCache.RemoveAsync(key, cancellationToken); | |
CacheKeys.TryRemove(key, out _); | |
} | |
public async Task RemoveByPrefixAsync(string prefixKey, CancellationToken cancellationToken = default) | |
{ | |
var tasks = CacheKeys | |
.Keys | |
.Where(k => k.StartsWith(prefixKey)) | |
.Select(k => RemoveAsync(k, cancellationToken)); | |
await Task.WhenAll(tasks); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MediatR; | |
namespace DomainDrivenDesignExample.Api.Features.Common.Messaging; | |
public interface ICachableQuery<out TResponse> : IRequest<TResponse> | |
{ | |
public string QueryKey { get; } | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MediatR; | |
namespace DomainDrivenDesignExample.Api.Features.Common.Messaging; | |
public interface ICachableQueryHandler<in TQuery, TResponse> : IRequestHandler<TQuery, TResponse> | |
where TQuery : ICachableQuery<TResponse>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace DomainDrivenDesignExample.Api.Features.Common.Caching; | |
internal interface ICacheService | |
{ | |
Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default) | |
where T : class; | |
Task<T> GetAsync<T>(string key, Func<Task<T>> valueFactory, CancellationToken cancellationToken = default) | |
where T : class; | |
Task SetAsync<T>(string key, T value, CancellationToken cancellationToken = default) | |
where T : class; | |
Task RemoveAsync(string key, CancellationToken cancellationToken = default); | |
Task RemoveByPrefixAsync(string prefixKey, CancellationToken cancellationToken = default); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MediatR; | |
namespace DomainDrivenDesignExample.Api.Features.Common.Messaging; | |
public interface IQuery<out TResponse> : IRequest<TResponse>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MediatR; | |
namespace DomainDrivenDesignExample.Api.Features.Common.Messaging; | |
public interface IQueryHandler<in TQuery, TResponse> : IRequestHandler<TQuery, TResponse> | |
where TQuery : IQuery<TResponse>; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment