Last active
March 29, 2025 18:54
-
-
Save mrmyroll2/94bcd4031576b9417736a7d70579ac2c to your computer and use it in GitHub Desktop.
Error implementing `HybridCache` and `IDistributedCache` for query caching with MediatR
This file contains hidden or 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
internal sealed class CacheService( | |
IMemoryCache memoryCache, | |
HybridCache hybridCache, | |
IDistributedCache distributedCache | |
) : ICacheService | |
{ | |
public async Task<T?> GetOrCreateAsync<T>( | |
string key, | |
Func<CancellationToken, Task<T>> factory, | |
TimeSpan? expiration = null, | |
CancellationToken cancellationToken = default) | |
{ | |
// IMemoryCache | |
T result2 = await memoryCache.GetOrCreateAsync( | |
key, | |
entry => | |
{ | |
entry.SetAbsoluteExpiration(expiration ?? TimeSpan.FromMinutes(5)); | |
return factory(cancellationToken); | |
}); | |
// HybridCache | |
var result = await hybridCache.GetOrCreateAsync( | |
key, | |
(cancel) => | |
{ | |
return new ValueTask<T>(factory(cancel)); | |
}, | |
cancellationToken: cancellationToken); | |
// IDistributedCache | |
var cachedData = await distributedCache.GetStringAsync(key, cancellationToken); | |
if (!string.IsNullOrEmpty(cachedData)) | |
{ | |
return JsonSerializer.Deserialize<T>(cachedData)!; | |
} | |
T result3 = await factory(cancellationToken); | |
var serializedData = JsonSerializer.Serialize(result); | |
await distributedCache.SetStringAsync(key, serializedData, cancellationToken); | |
return result; | |
} | |
} |
This file contains hidden or 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
public sealed record GetMemberQuery(Guid Id) : ICachedQuery<GetMemberResponse> | |
{ | |
public string CacheKey => $"GetMemberQuery-{Id}"; | |
public TimeSpan? Expiration => TimeSpan.FromMinutes(5); | |
} | |
public sealed record GetMemberResponse(string MemberName, | |
string IdentityNumber, | |
DateOnly EffectiveDate, | |
DateOnly ExpiryDate); | |
internal sealed class GetMemberQueryHandler( | |
IMemberRepository memberRepository) | |
: IQueryHandler<GetMemberQuery, GetMemberResponse> | |
{ | |
public async Task<Result<GetMemberResponse>> Handle(GetMemberQuery request, | |
CancellationToken cancellationToken) | |
{ | |
var data = await memberRepository.GetMemberById(request.Id, cancellationToken); | |
if (data is null) | |
{ | |
return Result.Failure<GetMemberResponse>(MemberError.MemberNotFound(request.Id)); | |
} | |
return new GetMemberResponse(data.MemberName, | |
data.IdentityNumber, | |
data.EffectiveDate, | |
data.ExpiryDate); | |
} | |
} |
This file contains hidden or 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
public class Result | |
{ | |
public Result(bool isSuccess, Error error) | |
{ | |
if (isSuccess && error != Error.None || | |
!isSuccess && error == Error.None) | |
{ | |
throw new ArgumentException("Invalid error", nameof(error)); | |
} | |
IsSuccess = isSuccess; | |
Error = error; | |
} | |
public bool IsSuccess { get; } | |
public bool IsFailure => !IsSuccess; | |
public Error Error { get; } | |
public static Result Success() => new(true, Error.None); | |
public static Result<TValue> Success<TValue>(TValue value) => | |
new(value, true, Error.None); | |
public static Result Failure(Error error) => new(false, error); | |
public static Result<TValue> Failure<TValue>(Error error) => | |
new(default, false, error); | |
} | |
public class Result<TValue> : Result | |
{ | |
private readonly TValue? _value; | |
public Result(TValue? value, bool isSuccess, Error error) | |
: base(isSuccess, error) | |
{ | |
_value = value; | |
} | |
// The problem is here | |
// it hit the throw | |
[NotNull] | |
public TValue Value => IsSuccess | |
? _value! | |
: throw new InvalidOperationException("The value of a failure result can't be accessed."); | |
public static implicit operator Result<TValue>(TValue? value) => | |
value is not null ? Success(value) : Failure<TValue>(Error.NullValue); | |
public static Result<TValue> ValidationFailure(Error error) => | |
new(default, false, error); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment