< Summary

Information
Class: Dom.Mediator.Implementation.Mediator
Assembly: Dom.Mediator
File(s): /home/runner/work/dom.mediator/dom.mediator/Dom.Mediator/Mediator.cs
Line coverage
70%
Covered lines: 47
Uncovered lines: 20
Coverable lines: 67
Total lines: 167
Line coverage: 70.1%
Branch coverage
62%
Covered branches: 34
Total branches: 54
Branch coverage: 62.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
RegisterHandlers(...)77.27%242283.33%
AddBehaviour(...)0%620%
Send()50%5455.55%
Send()50%5455.55%
GetHandler(...)100%44100%
GetHandlerType(...)50%2280%

File(s)

/home/runner/work/dom.mediator/dom.mediator/Dom.Mediator/Mediator.cs

#LineLine coverage
 1using Dom.Mediator.Abstractions;
 2using Dom.Mediator.Models;
 3using Microsoft.Extensions.DependencyInjection;
 4using System.Data;
 5using System.Reflection;
 6
 7namespace Dom.Mediator.Implementation;
 8
 9public class Mediator : IMediator
 10{
 11    private const int BEHAVIOUR_COMMAND_ARGS = 1;
 12    private const int BEHAVIOUR_QUERY_ARGS = 2;
 13
 1214    private readonly Dictionary<Type, Type> _queryHandler = new();
 1215    private readonly Dictionary<Type, Type> _commandHandler = new();
 16    private readonly IServiceProvider _serviceProvider;
 17
 1218    private readonly List<Type> _behaviours = new();
 19
 1220    public Mediator(IServiceProvider serviceProvider)
 21    {
 1222        _serviceProvider = serviceProvider;
 1223    }
 24
 25    #region INIT
 26    public void RegisterHandlers(params Assembly[] assemblies)
 27    {
 2428        var types = assemblies.SelectMany(a => a.GetTypes())
 24029                              .Where(t => !t.IsAbstract && !t.IsInterface)
 1230                              .ToList();
 31
 50432        foreach (var type in types)
 33        {
 93634            foreach (var iface in type.GetInterfaces())
 35            {
 22836                if (!iface.IsGenericType) continue;
 37
 12038                var def = iface.GetGenericTypeDefinition();
 39
 12040                if (def == typeof(IQueryHandler<,>))
 41                {
 1242                    var requestType = iface.GetGenericArguments()[0];
 43
 1244                    if (requestType.IsNotPublic)
 045                        throw new MediatorException($"The type '{type}' which defines the 'Query' must be declared as pu
 46
 1247                    if (type.IsNotPublic)
 048                        throw new MediatorException($"The type '{type}' which implements a 'Query Handler' must be decla
 49
 1250                    _queryHandler[requestType] = type;
 51                }
 10852                else if (
 10853                    def == typeof(ICommandHandler<>) ||
 10854                    def == typeof(ICommandHandler<,>))
 55                {
 2456                    var commandType = iface.GetGenericArguments()[0];
 57
 2458                    if (commandType.IsNotPublic)
 059                        throw new MediatorException($"The type '{commandType}' which defines the 'Command' must be decla
 60
 2461                    if (type.IsNotPublic)
 062                        throw new MediatorException($"The type '{type}' which implements a 'Command Handler' must be dec
 63
 2464                    _commandHandler[commandType] = type;
 65                }
 66            }
 67        }
 1268    }
 69
 70    public void AddBehaviour(Type behaviourType)
 71    {
 072        if (!behaviourType.IsGenericType)
 073            throw new ArgumentException("Behaviour type must be generic", nameof(behaviourType));
 74
 075        var genericTypeDefinition = behaviourType.GetGenericTypeDefinition();
 076        _behaviours.Add(genericTypeDefinition);
 077    }
 78
 79    #endregion
 80
 81    #region MEDIATOR METHODS
 82    public async Task<Result<TResponse>> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToke
 83    {
 884        var handlerRes = GetHandlerType(request);
 685        var requestType = handlerRes.Item1;
 86
 1287        RequestHandlerDelegate<TResponse> next = () => handlerRes.Item2.Handle((dynamic)request, cancellationToken);
 88
 89        // Apply behaviors in reverse order (so the first registered behavior is the outermost)
 1290        foreach (var behaviourType in _behaviours.AsEnumerable().Reverse())
 91        {
 92            // Check if this behavior has the right arity (2 type parameters)
 093            if (behaviourType.GetGenericArguments().Length == BEHAVIOUR_QUERY_ARGS)
 94            {
 095                var concreteBehaviourType = behaviourType.MakeGenericType(requestType, typeof(TResponse));
 96
 097                object? behaviour = ActivatorUtilities.CreateInstance(_serviceProvider, concreteBehaviourType);
 98
 099                var currentNext = next;
 0100                next = () => ((dynamic)behaviour).Handle((dynamic)request, cancellationToken, currentNext);
 101            }
 102            // Skip behaviors with different arity
 103        }
 104
 6105        return await next();
 6106    }
 107
 108    /// <summary>
 109    /// Sends a command that doesn't return a value
 110    /// </summary>
 111    public async Task<Result> Send(ICommand command, CancellationToken cancellationToken = default)
 112    {
 4113        var handlerRes = GetHandlerType(command);
 4114        var commandType = handlerRes.Item1;
 115
 8116        CommandHandlerDelegate next = () => handlerRes.Item2.Handle((dynamic)command, cancellationToken);
 117
 118        // Apply behaviors in reverse order (so the first registered behavior is the outermost)
 8119        foreach (var behaviourType in _behaviours.AsEnumerable().Reverse())
 120        {
 121            // Check if this behavior has the right arity (1 type parameter)
 0122            if (behaviourType.GetGenericArguments().Length == BEHAVIOUR_COMMAND_ARGS)
 123            {
 0124                var concreteBehaviourType = behaviourType.MakeGenericType(commandType);
 125
 0126                object? behaviour = ActivatorUtilities.CreateInstance(_serviceProvider, concreteBehaviourType);
 127
 0128                var currentNext = next;
 0129                next = () => ((dynamic)behaviour).Handle((dynamic)command, cancellationToken, currentNext);
 130            }
 131            // Skip behaviors with different arity
 132        }
 133
 4134        return await next();
 4135    }
 136    #endregion
 137
 138    #region PRIVATE
 139
 140    private dynamic GetHandler(Type requestType)
 141    {
 12142        if (_queryHandler.TryGetValue(requestType, out var handlerType))
 143        {
 2144            return ActivatorUtilities.CreateInstance(_serviceProvider, handlerType);
 145        }
 10146        else if (_commandHandler.TryGetValue(requestType, out var commandHandlerType))
 147        {
 8148            return ActivatorUtilities.CreateInstance(_serviceProvider, commandHandlerType);
 149        }
 150
 2151        throw new MediatorException($"Handler not registered for type: {requestType.Name}");
 152    }
 153
 154    private (Type, dynamic) GetHandlerType(object request)
 155    {
 12156        var requestType = request.GetType();
 12157        dynamic handler = GetHandler(requestType);
 158
 10159        if (handler is null)
 0160            throw new MediatorException($"Handler not registered for type: {requestType.Name}");
 161
 10162        return (requestType, handler);
 163    }
 164
 165
 166    #endregion
 167}