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.
54 lines
3.2 KiB
C#
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);
|
|
} |