Files
EnvelopeGenerator/EnvelopeGenerator.Application/Common/Extensions/MediatorExtensions.cs
TekH 6c54473d5a Refactor MediatorExtensions for flexible exception handling
Generalize null/empty response handling with SendOrThrowAsync<TResponse, TException>, allowing custom exceptions via a factory delegate. SendOrNotFoundAsync now wraps this method for NotFoundException. Improves type safety, flexibility, and XML docs; avoids treating strings as collections.
2026-04-08 13:44:19 +02:00

54 lines
3.2 KiB
C#

using System.Collections;
using DigitalData.Core.Exceptions;
using MediatR;
namespace EnvelopeGenerator.Application.Common.Extensions;
/// <summary>
/// Extension methods for <see cref="IMediator"/> that enforce non-null and non-empty responses.
/// </summary>
public static class MediatorExtensions
{
/// <summary>
/// Sends a request via MediatR and throws a custom exception produced by <paramref name="exceptionFactory"/>
/// when the response is <c>null</c> or an empty collection.
/// </summary>
/// <typeparam name="TResponse">The expected response type.</typeparam>
/// <typeparam name="TException">The exception type to throw.</typeparam>
/// <param name="mediator">The mediator instance.</param>
/// <param name="request">The MediatR request whose response may be <c>null</c>.</param>
/// <param name="exceptionFactory">A factory that creates the exception to throw when the response is absent.</param>
/// <param name="cancel">Cancellation token.</param>
/// <returns>A guaranteed non-null <typeparamref name="TResponse"/>.</returns>
/// <exception cref="Exception">The exception produced by <paramref name="exceptionFactory"/>.</exception>
public static async Task<TResponse> SendOrThrowAsync<TResponse, TException>(this IMediator mediator, IRequest<TResponse?> request, Func<TException> exceptionFactory, CancellationToken cancel = default)
where TException : Exception
{
if (await mediator.Send(request, cancel) is TResponse res)
{
if (res is not string && res is IEnumerable enumerable && !enumerable.Cast<object>().Any())
throw exceptionFactory();
return res;
}
throw exceptionFactory();
}
/// <summary>
/// Sends a request via MediatR and throws <see cref="NotFoundException"/> when the response is <c>null</c> or an empty collection.
/// </summary>
/// <typeparam name="TResponse">The expected response type.</typeparam>
/// <param name="mediator">The mediator instance.</param>
/// <param name="request">The MediatR request whose response may be <c>null</c>.</param>
/// <param name="exceptionMessage">Optional message for the <see cref="NotFoundException"/>.</param>
/// <param name="cancel">Cancellation token.</param>
/// <returns>A guaranteed non-null <typeparamref name="TResponse"/>.</returns>
/// <exception cref="NotFoundException">Thrown when the response is <c>null</c> or an empty collection.</exception>
public static async Task<TResponse> SendOrNotFoundAsync<TResponse>(this IMediator mediator, IRequest<TResponse?> request, string? exceptionMessage, CancellationToken cancel = default)
=> await mediator.SendOrThrowAsync(request, () => new NotFoundException(exceptionMessage ?? $"The requested resource of type {typeof(TResponse).Name} was not found."), cancel);
/// <inheritdoc cref="SendOrNotFoundAsync{TResponse}(IMediator, IRequest{TResponse}, string, CancellationToken)"/>
public static async Task<TResponse> SendOrNotFoundAsync<TResponse>(this IMediator mediator, IRequest<TResponse?> request, CancellationToken cancel = default)
=> await mediator.SendOrNotFoundAsync(request, null, cancel);
}