< Summary

Information
Class: ArturRios.Mediator.Command.CommandMediator
Assembly: ArturRios.Mediator
File(s): /home/ci/agent/workspace/dotnet-mediator-docs_main/app/src/Command/CommandMediator.cs
Line coverage
100%
Covered lines: 13
Uncovered lines: 0
Coverable lines: 13
Total lines: 84
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage
100%
Covered methods: 3
Fully covered methods: 3
Total methods: 3
Method coverage: 100%
Full method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ExecuteCommand(...)100%11100%
ExecuteCommandAsync()100%11100%

File(s)

/home/ci/agent/workspace/dotnet-mediator-docs_main/app/src/Command/CommandMediator.cs

#LineLine coverage
 1using ArturRios.Mediator.Command.Interfaces;
 2using ArturRios.Output;
 3using Microsoft.Extensions.DependencyInjection;
 4
 5namespace ArturRios.Mediator.Command;
 6
 7/// <summary>
 8/// Dispatches commands to their registered handlers. For each call a new dependency
 9/// injection scope is created, the matching handler is resolved from it, and the
 10/// handler is invoked.
 11/// </summary>
 12/// <remarks>
 13/// Register this mediator and the command handlers in your dependency injection
 14/// container. Because a fresh scope is created per execution, scoped dependencies of a
 15/// handler (such as a database context) are isolated to a single command execution.
 16/// </remarks>
 17/// <param name="scopeFactory">
 18/// The factory used to create a dependency injection scope for resolving handlers.
 19/// </param>
 3820public class CommandMediator(IServiceScopeFactory scopeFactory)
 21{
 22    /// <summary>
 23    /// Resolves the synchronous handler for the given command and executes it.
 24    /// </summary>
 25    /// <typeparam name="TCommand">The command type to execute.</typeparam>
 26    /// <typeparam name="TOutput">The type of the data payload produced by the handler.</typeparam>
 27    /// <param name="command">The command instance to execute.</param>
 28    /// <returns>The <see cref="DataOutput{T}"/> returned by the resolved handler.</returns>
 29    /// <exception cref="ArgumentNullException"><paramref name="command"/> is <see langword="null"/>.</exception>
 30    /// <exception cref="InvalidOperationException">
 31    /// Thrown when no <see cref="ICommandHandler{TCommand, TOutput}"/> is registered for the
 32    /// requested command and output types.
 33    /// </exception>
 34    public DataOutput<TOutput?> ExecuteCommand<TCommand, TOutput>(TCommand command)
 35        where TCommand : BaseCommand
 36        where TOutput : CommandOutput
 37    {
 738        ArgumentNullException.ThrowIfNull(command);
 39
 640        using var scoped = scopeFactory.CreateScope();
 41
 642        var handler = scoped.ServiceProvider.GetRequiredService<ICommandHandler<TCommand, TOutput>>();
 43
 544        return handler.Handle(command);
 545    }
 46
 47    /// <summary>
 48    /// Resolves the asynchronous handler for the given command and executes it.
 49    /// </summary>
 50    /// <typeparam name="TCommand">The command type to execute.</typeparam>
 51    /// <typeparam name="TOutput">The type of the data payload produced by the handler.</typeparam>
 52    /// <param name="command">The command instance to execute.</param>
 53    /// <param name="cancellationToken">Cancels the execution, and the dispatch itself.</param>
 54    /// <returns>
 55    /// A task that resolves to the <see cref="DataOutput{T}"/> returned by the resolved handler.
 56    /// </returns>
 57    /// <exception cref="ArgumentNullException"><paramref name="command"/> is <see langword="null"/>.</exception>
 58    /// <exception cref="InvalidOperationException">
 59    /// Thrown when no <see cref="ICommandHandlerAsync{TCommand, TOutput}"/> is registered for the
 60    /// requested command and output types.
 61    /// </exception>
 62    /// <exception cref="OperationCanceledException">
 63    /// <paramref name="cancellationToken"/> was already canceled. The check happens before the scope is
 64    /// created, so a canceled dispatch costs nothing.
 65    /// </exception>
 66    public async Task<DataOutput<TOutput?>> ExecuteCommandAsync<TCommand, TOutput>(
 67        TCommand command,
 68        CancellationToken cancellationToken = default)
 69        where TCommand : BaseCommand
 70        where TOutput : CommandOutput
 71    {
 4072        ArgumentNullException.ThrowIfNull(command);
 3973        cancellationToken.ThrowIfCancellationRequested();
 74
 75        // Disposed asynchronously: a scoped dependency that only implements IAsyncDisposable makes a
 76        // synchronous Dispose throw, after the handler has already run.
 3777        var scoped = scopeFactory.CreateAsyncScope();
 3778        await using var scopeDisposal = scoped.ConfigureAwait(false);
 79
 3780        var handler = scoped.ServiceProvider.GetRequiredService<ICommandHandlerAsync<TCommand, TOutput>>();
 81
 3582        return await handler.HandleAsync(command, cancellationToken).ConfigureAwait(false);
 3583    }
 84}