< Summary

Information
Class: NexMediator.Pipeline.Behaviors.CachingBehavior<T1, T2>
Assembly: NexMediator.Pipeline
File(s): /home/runner/work/NexMediator/NexMediator/src/NexMediator.Pipeline/Behaviors/CachingBehavior.cs
Line coverage
83%
Covered lines: 41
Uncovered lines: 8
Coverable lines: 49
Total lines: 95
Line coverage: 83.6%
Branch coverage
61%
Covered branches: 11
Total branches: 18
Branch coverage: 61.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%44100%
Handle()50%151481.81%

File(s)

/home/runner/work/NexMediator/NexMediator/src/NexMediator.Pipeline/Behaviors/CachingBehavior.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2using NexMediator.Abstractions.Exceptions;
 3using NexMediator.Abstractions.Interfaces;
 4
 5namespace NexMediator.Pipeline.Behaviors;
 6/// <summary>
 7/// A pipeline behavior that adds caching to supported requests.
 8/// Also supports cache invalidation for requests implementing <see cref="IInvalidateCacheableRequest"/>.
 9/// </summary>
 10/// <typeparam name="TRequest">The type of the request.</typeparam>
 11/// <typeparam name="TResponse">The type of the response.</typeparam>
 12public class CachingBehavior<TRequest, TResponse> : INexPipelineBehavior<TRequest, TResponse>
 13    where TRequest : INexRequest<TResponse>
 14{
 15    private readonly ICache _cache;
 16    private readonly ILogger<CachingBehavior<TRequest, TResponse>> _logger;
 17
 18    /// <summary>
 19    /// Creates a new instance of <see cref="CachingBehavior{TRequest, TResponse}"/>.
 20    /// </summary>
 21    /// <param name="cache">The cache service.</param>
 22    /// <param name="logger">The logger instance.</param>
 2423    public CachingBehavior(ICache cache, ILogger<CachingBehavior<TRequest, TResponse>> logger)
 2424    {
 2425        _cache = cache ?? throw new ArgumentNullException(nameof(cache));
 2326        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 2227    }
 28
 29    /// <inheritdoc />
 30    public async Task<TResponse> Handle(
 31        TRequest request,
 32        RequestHandlerDelegate<TResponse> next,
 33        CancellationToken cancellationToken)
 1134    {
 35        //Invalidate cache for requests that require it
 1136        if (request is IInvalidateCacheableRequest inv)
 037        {
 038            foreach (var key in inv.KeysToInvalidate)
 039                await _cache.RemoveAsync(key, cancellationToken);
 040            foreach (var prefix in inv.PrefixesToInvalidate)
 041                await _cache.RemoveByPrefixAsync(prefix, cancellationToken);
 042        }
 43
 44        //Proceed with caching logic
 1145        if (request is not ICacheableRequest<TResponse> cacheable)
 146        {
 147            return await next();
 48        }
 49
 1050        var cacheKey = cacheable.CacheKey;
 1051        TResponse? cached = default;
 52
 53        try
 1054        {
 1055            cached = await _cache.GetAsync<TResponse>(cacheKey, cancellationToken);
 956        }
 157        catch (Exception ex)
 158        {
 159            _logger.LogWarning(ex, "Cache read failed for key {CacheKey}", cacheKey);
 160        }
 61
 1062        if (cached != null)
 263        {
 264            _logger.LogDebug("Cache hit for {RequestType} [Key: {CacheKey}]", typeof(TRequest).Name, cacheKey);
 265            return cached;
 66        }
 67
 868        _logger.LogDebug("Cache miss for {RequestType} [Key: {CacheKey}]", typeof(TRequest).Name, cacheKey);
 69
 870        var response = await next();
 71
 872        if (response == null)
 073        {
 074            throw new NexMediatorException($"Handler returned null for request type {typeof(TRequest).Name}");
 75        }
 76
 877        int? ttl = cacheable.Expiration.HasValue
 878            ? (int)cacheable.Expiration.Value.TotalSeconds
 879            : null;
 80
 81        try
 882        {
 883            await _cache.SetAsync(cacheKey, response, ttl, cancellationToken);
 784            _logger.LogDebug(
 785                "Response cached for {RequestType} [Key: {CacheKey}, TTL: {TTL}s]",
 786                typeof(TRequest).Name, cacheKey, ttl);
 787        }
 188        catch (Exception ex)
 189        {
 190            _logger.LogWarning(ex, "Cache write failed for key {CacheKey}", cacheKey);
 191        }
 92
 893        return response;
 1194    }
 95}