From 43fb46f39020ab4880fefe75fa2315351f347742 Mon Sep 17 00:00:00 2001 From: zach painter Date: Fri, 22 Mar 2024 20:40:20 -0700 Subject: [PATCH 1/4] Add logic to register generic handlers.. (Handlers with an open generic request type) --- src/MediatR/Registration/ServiceRegistrar.cs | 100 ++++++++++++++++-- .../AssemblyResolutionTests.cs | 29 ++++- .../MicrosoftExtensionsDI/Handlers.cs | 42 ++++++++ test/MediatR.Tests/SendTests.cs | 97 ++++++++++++++++- 4 files changed, 254 insertions(+), 14 deletions(-) diff --git a/src/MediatR/Registration/ServiceRegistrar.cs b/src/MediatR/Registration/ServiceRegistrar.cs index e4bba2b5..8fd5bf96 100644 --- a/src/MediatR/Registration/ServiceRegistrar.cs +++ b/src/MediatR/Registration/ServiceRegistrar.cs @@ -65,21 +65,37 @@ public static void AddMediatRClasses(IServiceCollection services, MediatRService bool addIfAlreadyExists, MediatRServiceConfiguration configuration) { - var concretions = new List(); + var concretions = new List(); var interfaces = new List(); - foreach (var type in assembliesToScan.SelectMany(a => a.DefinedTypes).Where(t => !t.IsOpenGeneric()).Where(configuration.TypeEvaluator)) + var genericConcretions = new List(); + var genericInterfaces = new List(); + + var types = assembliesToScan + .SelectMany(a => a.DefinedTypes) + .Where(t => t.IsConcrete() && t.FindInterfacesThatClose(openRequestInterface).Any()) + .Where(configuration.TypeEvaluator) + .ToList(); + + foreach (var type in types) { var interfaceTypes = type.FindInterfacesThatClose(openRequestInterface).ToArray(); - if (!interfaceTypes.Any()) continue; - if (type.IsConcrete()) + if (!type.IsOpenGeneric()) { concretions.Add(type); - } - foreach (var interfaceType in interfaceTypes) + foreach (var interfaceType in interfaceTypes) + { + interfaces.Fill(interfaceType); + } + } + else { - interfaces.Fill(interfaceType); + genericConcretions.Add(type); + foreach (var interfaceType in interfaceTypes) + { + genericInterfaces.Fill(interfaceType); + } } } @@ -111,6 +127,12 @@ public static void AddMediatRClasses(IServiceCollection services, MediatRService AddConcretionsThatCouldBeClosed(@interface, concretions, services); } } + + foreach (var @interface in genericInterfaces) + { + var exactMatches = genericConcretions.Where(x => x.CanBeCastTo(@interface)).ToList(); + AddAllConcretionsThatClose(@interface, exactMatches, services, assembliesToScan); + } } private static bool IsMatchingWithInterface(Type? handlerType, Type handlerInterface) @@ -150,6 +172,62 @@ private static void AddConcretionsThatCouldBeClosed(Type @interface, List } } + private static (Type Service, Type Implementation) GetConcreteRegistrationTypes(Type openRequestHandlerInterface, Type concreteGenericTRequest, Type openRequestHandlerImplementation) + { + var closingType = concreteGenericTRequest.GetGenericArguments().First(); + + var concreteTResponse = concreteGenericTRequest.GetInterfaces() + .FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IRequest<>)) + ?.GetGenericArguments() + .FirstOrDefault(); + + var typeDefinition = openRequestHandlerInterface.GetGenericTypeDefinition(); + + var serviceType = concreteTResponse != null ? + typeDefinition.MakeGenericType(concreteGenericTRequest, concreteTResponse) : + typeDefinition.MakeGenericType(concreteGenericTRequest); + + return (serviceType, openRequestHandlerImplementation.MakeGenericType(closingType)); + } + + private static List? GetConcreteRequestTypes(Type openRequestHandlerInterface, Type openRequestHandlerImplementation, IEnumerable assembliesToScan) + { + var constraints = openRequestHandlerImplementation.GetGenericArguments().First().GetGenericParameterConstraints(); + + var typesThatCanClose = assembliesToScan + .SelectMany(assembly => assembly.GetTypes()) + .Where(type => type.IsClass && !type.IsAbstract && constraints.All(constraint => constraint.IsAssignableFrom(type))) + .ToList(); + + var requestType = openRequestHandlerInterface.GenericTypeArguments.First(); + + if (requestType.IsGenericParameter) + return null; + + var requestGenericTypeDefinition = requestType.GetGenericTypeDefinition(); + + return typesThatCanClose.Select(type => requestGenericTypeDefinition.MakeGenericType(type)).ToList(); + } + + private static void AddAllConcretionsThatClose(Type openRequestInterface, List concretions, IServiceCollection services, IEnumerable assembliesToScan) + { + foreach (var concretion in concretions) + { + var concreteRequests = GetConcreteRequestTypes(openRequestInterface, concretion, assembliesToScan); + + if (concreteRequests is null) + continue; + + var registrationTypes = concreteRequests + .Select(concreteRequest => GetConcreteRegistrationTypes(openRequestInterface, concreteRequest, concretion)); + + foreach (var (Service, Implementation) in registrationTypes) + { + services.AddTransient(Service, Implementation); + } + } + } + internal static bool CouldCloseTo(this Type openConcretion, Type closedInterface) { var openInterface = closedInterface.GetGenericTypeDefinition(); @@ -259,8 +337,8 @@ public static void AddRequiredServices(IServiceCollection services, MediatRServi foreach (var serviceDescriptor in serviceConfiguration.BehaviorsToRegister) { services.TryAddEnumerable(serviceDescriptor); - } - + } + foreach (var serviceDescriptor in serviceConfiguration.StreamBehaviorsToRegister) { services.TryAddEnumerable(serviceDescriptor); @@ -270,7 +348,7 @@ public static void AddRequiredServices(IServiceCollection services, MediatRServi private static void RegisterBehaviorIfImplementationsExist(IServiceCollection services, Type behaviorType, Type subBehaviorType) { var hasAnyRegistrationsOfSubBehaviorType = services - .Where(service => !service.IsKeyedService) + .Where(service => !service.IsKeyedService) .Select(service => service.ImplementationType) .OfType() .SelectMany(type => type.GetInterfaces()) @@ -283,4 +361,4 @@ private static void RegisterBehaviorIfImplementationsExist(IServiceCollection se services.TryAddEnumerable(new ServiceDescriptor(typeof(IPipelineBehavior<,>), behaviorType, ServiceLifetime.Transient)); } } -} \ No newline at end of file +} diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs index f5175b91..d145b48f 100644 --- a/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs @@ -3,6 +3,7 @@ namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using Shouldly; @@ -11,6 +12,7 @@ namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; public class AssemblyResolutionTests { private readonly IServiceProvider _provider; + private readonly List _services; public AssemblyResolutionTests() { @@ -18,6 +20,7 @@ public AssemblyResolutionTests() services.AddSingleton(new Logger()); services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Ping).Assembly)); _provider = services.BuildServiceProvider(); + _services = services.ToList(); } [Fact] @@ -55,8 +58,32 @@ public void ShouldRequireAtLeastOneAssembly() { var services = new ServiceCollection(); - Action registration = () => services.AddMediatR(_ => {}); + Action registration = () => services.AddMediatR(_ => { }); registration.ShouldThrow(); } + + [Fact] + public void ShouldResolveGenericVoidRequestHandler() + { + _provider.GetService>>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveGenericReturnTypeRequestHandler() + { + _provider.GetService, string>>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveGenericPingRequestHandler() + { + _provider.GetService, Pong>>().ShouldNotBeNull(); + } + + [Fact] + public void ShouldResolveVoidGenericPingRequestHandler() + { + _provider.GetService>>().ShouldNotBeNull(); + } } \ No newline at end of file diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs index f71c407e..5dbe0c1e 100644 --- a/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/Handlers.cs @@ -213,6 +213,48 @@ public Task Send(TRequest request, CancellationToken cancellationToken throw new System.NotImplementedException(); } } + + interface ITypeArgument { } + class ConcreteTypeArgument : ITypeArgument { } + class OpenGenericVoidRequest : IRequest + where T : class, ITypeArgument + { } + class OpenGenericVoidRequestHandler : IRequestHandler> + where T : class, ITypeArgument + { + public Task Handle(OpenGenericVoidRequest request, CancellationToken cancellationToken) => Task.CompletedTask; + } + class OpenGenericReturnTypeRequest : IRequest + where T : class, ITypeArgument + { } + class OpenGenericReturnTypeRequestHandler : IRequestHandler, string> + where T : class, ITypeArgument + { + public Task Handle(OpenGenericReturnTypeRequest request, CancellationToken cancellationToken) => Task.FromResult(nameof(request)); + } + + public class GenericPing : IRequest + where T : Pong + { + public T? Pong { get; set; } + } + + public class GenericPingHandler : IRequestHandler, T> + where T : Pong + { + public Task Handle(GenericPing request, CancellationToken cancellationToken) => Task.FromResult(request.Pong!); + } + + public class VoidGenericPing : IRequest + where T : Pong + { } + + public class VoidGenericPingHandler : IRequestHandler> + where T : Pong + { + public Task Handle(VoidGenericPing request, CancellationToken cancellationToken) => Task.CompletedTask; + } + } namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests.Included diff --git a/test/MediatR.Tests/SendTests.cs b/test/MediatR.Tests/SendTests.cs index 7ca41fe7..8e38558b 100644 --- a/test/MediatR.Tests/SendTests.cs +++ b/test/MediatR.Tests/SendTests.cs @@ -52,6 +52,44 @@ public Task Handle(VoidPing request, CancellationToken cancellationToken) } } + public class GenericPing : IRequest + where T : Pong + { + public T? Pong { get; set; } + } + + public class GenericPingHandler : IRequestHandler, T> + where T : Pong + { + private readonly Dependency _dependency; + public GenericPingHandler(Dependency dependency) => _dependency = dependency; + + public Task Handle(GenericPing request, CancellationToken cancellationToken) + { + _dependency.Called = true; + request.Pong!.Message += " Pong"; + return Task.FromResult(request.Pong!); + } + } + + public class VoidGenericPing : IRequest + where T : Pong + { } + + public class VoidGenericPingHandler : IRequestHandler> + where T : Pong + { + private readonly Dependency _dependency; + public VoidGenericPingHandler(Dependency dependency) => _dependency = dependency; + + public Task Handle(VoidGenericPing request, CancellationToken cancellationToken) + { + _dependency.Called = true; + + return Task.CompletedTask; + } + } + [Fact] public async Task Should_resolve_main_handler() { @@ -127,8 +165,8 @@ public async Task Should_resolve_main_handler_via_dynamic_dispatch() [Fact] public async Task Should_resolve_main_void_handler_via_dynamic_dispatch() { - var dependency = new Dependency(); - + var dependency = new Dependency(); + var container = new Container(cfg => { cfg.Scan(scanner => @@ -213,4 +251,59 @@ public async Task Should_raise_execption_on_null_request() await Should.ThrowAsync(async () => await mediator.Send(default!)); } + + [Fact] + public async Task Should_resolve_generic_handler_by_given_interface() + { + var dependency = new Dependency(); + var container = new Container(cfg => + { + cfg.Scan(scanner => + { + scanner.AssemblyContainingType(typeof(PublishTests)); + scanner.IncludeNamespace(typeof(GenericPing<>).Namespace); + scanner.WithDefaultConventions(); + scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); + }); + cfg.ForSingletonOf().Use(dependency); + cfg.For().Use(); + cfg.For, Pong>>().Use>(); + }); + + var mediator = container.GetInstance(); + + object request = new GenericPing { Pong = new Pong { Message = "Ping" } }; + var result = await mediator.Send(request); + + var pong = result.ShouldBeOfType(); + pong.Message.ShouldBe("Ping Pong"); + + dependency.Called.ShouldBeTrue(); + } + + [Fact] + public async Task Should_resolve_generic_void_handler_by_given_interface() + { + var dependency = new Dependency(); + var container = new Container(cfg => + { + cfg.Scan(scanner => + { + scanner.AssemblyContainingType(typeof(PublishTests)); + scanner.IncludeNamespace(typeof(VoidGenericPing<>).Namespace); + scanner.WithDefaultConventions(); + scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); + }); + cfg.ForSingletonOf().Use(dependency); + cfg.For().Use(); + cfg.For>>().Use>(); + }); + + var mediator = container.GetInstance(); + + var request = new VoidGenericPing(); + await mediator.Send(request); + + dependency.Called.ShouldBeTrue(); + } } \ No newline at end of file From 3bf18ee1fdeb1e7c11543198645b9c90cb404b16 Mon Sep 17 00:00:00 2001 From: zach painter Date: Fri, 22 Mar 2024 20:43:55 -0700 Subject: [PATCH 2/4] remove debugging field --- .../MicrosoftExtensionsDI/AssemblyResolutionTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs b/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs index d145b48f..dbbcaefc 100644 --- a/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs +++ b/test/MediatR.Tests/MicrosoftExtensionsDI/AssemblyResolutionTests.cs @@ -12,7 +12,6 @@ namespace MediatR.Extensions.Microsoft.DependencyInjection.Tests; public class AssemblyResolutionTests { private readonly IServiceProvider _provider; - private readonly List _services; public AssemblyResolutionTests() { @@ -20,7 +19,6 @@ public AssemblyResolutionTests() services.AddSingleton(new Logger()); services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Ping).Assembly)); _provider = services.BuildServiceProvider(); - _services = services.ToList(); } [Fact] From 338482e5876c44711942d1591c43c57467763480 Mon Sep 17 00:00:00 2001 From: zach painter Date: Wed, 17 Apr 2024 22:12:28 -0700 Subject: [PATCH 3/4] convert to use microsoft .net default container --- test/MediatR.Tests/SendTests.cs | 64 ++++++++++++--------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/test/MediatR.Tests/SendTests.cs b/test/MediatR.Tests/SendTests.cs index 8e38558b..ac772b3b 100644 --- a/test/MediatR.Tests/SendTests.cs +++ b/test/MediatR.Tests/SendTests.cs @@ -7,9 +7,24 @@ namespace MediatR.Tests; using Shouldly; using Lamar; using Xunit; - +using Microsoft.Extensions.DependencyInjection; + public class SendTests { + private readonly IServiceProvider _serviceProvider; + private Dependency _dependency; + private readonly IMediator _mediator; + + public SendTests() + { + _dependency = new Dependency(); + var services = new ServiceCollection(); + services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(typeof(Ping).Assembly)); + services.AddSingleton(_dependency); + _serviceProvider = services.BuildServiceProvider(); + _mediator = _serviceProvider.GetService()!; + + } public class Ping : IRequest { @@ -62,6 +77,7 @@ public class GenericPingHandler : IRequestHandler, T> where T : Pong { private readonly Dependency _dependency; + public GenericPingHandler(Dependency dependency) => _dependency = dependency; public Task Handle(GenericPing request, CancellationToken cancellationToken) @@ -254,56 +270,22 @@ public async Task Should_raise_execption_on_null_request() [Fact] public async Task Should_resolve_generic_handler_by_given_interface() - { - var dependency = new Dependency(); - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespace(typeof(GenericPing<>).Namespace); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.ForSingletonOf().Use(dependency); - cfg.For().Use(); - cfg.For, Pong>>().Use>(); - }); - - var mediator = container.GetInstance(); - - object request = new GenericPing { Pong = new Pong { Message = "Ping" } }; - var result = await mediator.Send(request); + { + var request = new GenericPing { Pong = new Pong { Message = "Ping" } }; + var result = await _mediator.Send(request); var pong = result.ShouldBeOfType(); pong.Message.ShouldBe("Ping Pong"); - dependency.Called.ShouldBeTrue(); + _dependency.Called.ShouldBeTrue(); } [Fact] public async Task Should_resolve_generic_void_handler_by_given_interface() { - var dependency = new Dependency(); - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespace(typeof(VoidGenericPing<>).Namespace); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.ForSingletonOf().Use(dependency); - cfg.For().Use(); - cfg.For>>().Use>(); - }); - - var mediator = container.GetInstance(); - var request = new VoidGenericPing(); - await mediator.Send(request); + await _mediator.Send(request); - dependency.Called.ShouldBeTrue(); + _dependency.Called.ShouldBeTrue(); } } \ No newline at end of file From 1552d92a35081119a5fae9454b74b56ffdf046f6 Mon Sep 17 00:00:00 2001 From: zach painter Date: Wed, 17 Apr 2024 22:21:35 -0700 Subject: [PATCH 4/4] remove Lamar Dependency --- test/MediatR.Tests/SendTests.cs | 129 +++----------------------------- 1 file changed, 12 insertions(+), 117 deletions(-) diff --git a/test/MediatR.Tests/SendTests.cs b/test/MediatR.Tests/SendTests.cs index ac772b3b..b489a808 100644 --- a/test/MediatR.Tests/SendTests.cs +++ b/test/MediatR.Tests/SendTests.cs @@ -5,7 +5,6 @@ namespace MediatR.Tests; using System; using System.Threading.Tasks; using Shouldly; -using Lamar; using Xunit; using Microsoft.Extensions.DependencyInjection; @@ -109,21 +108,7 @@ public Task Handle(VoidGenericPing request, CancellationToken cancellationTok [Fact] public async Task Should_resolve_main_handler() { - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespaceContainingType(); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - - var response = await mediator.Send(new Ping { Message = "Ping" }); + var response = await _mediator.Send(new Ping { Message = "Ping" }); response.Message.ShouldBe("Ping Pong"); } @@ -131,48 +116,16 @@ public async Task Should_resolve_main_handler() [Fact] public async Task Should_resolve_main_void_handler() { - var dependency = new Dependency(); + await _mediator.Send(new VoidPing()); - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespaceContainingType(); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<>)); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.ForSingletonOf().Use(dependency); - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - - await mediator.Send(new VoidPing()); - - dependency.Called.ShouldBeTrue(); + _dependency.Called.ShouldBeTrue(); } [Fact] public async Task Should_resolve_main_handler_via_dynamic_dispatch() { - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespaceContainingType(); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - object request = new Ping { Message = "Ping" }; - var response = await mediator.Send(request); + var response = await _mediator.Send(request); var pong = response.ShouldBeOfType(); pong.Message.ShouldBe("Ping Pong"); @@ -181,50 +134,18 @@ public async Task Should_resolve_main_handler_via_dynamic_dispatch() [Fact] public async Task Should_resolve_main_void_handler_via_dynamic_dispatch() { - var dependency = new Dependency(); - - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespaceContainingType(); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<>)); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.ForSingletonOf().Use(dependency); - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - object request = new VoidPing(); - var response = await mediator.Send(request); + var response = await _mediator.Send(request); response.ShouldBeOfType(); - dependency.Called.ShouldBeTrue(); + _dependency.Called.ShouldBeTrue(); } [Fact] public async Task Should_resolve_main_handler_by_specific_interface() { - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespaceContainingType(); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<,>)); - }); - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - - var response = await mediator.Send(new Ping { Message = "Ping" }); + var response = await _mediator.Send(new Ping { Message = "Ping" }); response.Message.ShouldBe("Ping Pong"); } @@ -232,44 +153,18 @@ public async Task Should_resolve_main_handler_by_specific_interface() [Fact] public async Task Should_resolve_main_handler_by_given_interface() { - var dependency = new Dependency(); - var container = new Container(cfg => - { - cfg.Scan(scanner => - { - scanner.AssemblyContainingType(typeof(PublishTests)); - scanner.IncludeNamespaceContainingType(); - scanner.WithDefaultConventions(); - scanner.AddAllTypesOf(typeof(IRequestHandler<>)); - }); - cfg.ForSingletonOf().Use(dependency); - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - // wrap requests in an array, so this test won't break on a 'replace with var' refactoring var requests = new IRequest[] { new VoidPing() }; - await mediator.Send(requests[0]); + await _mediator.Send(requests[0]); - dependency.Called.ShouldBeTrue(); + _dependency.Called.ShouldBeTrue(); } [Fact] - public async Task Should_raise_execption_on_null_request() - { - var container = new Container(cfg => - { - cfg.For().Use(); - }); - - var mediator = container.GetInstance(); - - await Should.ThrowAsync(async () => await mediator.Send(default!)); - } + public Task Should_raise_execption_on_null_request() => Should.ThrowAsync(async () => await _mediator.Send(default!)); [Fact] - public async Task Should_resolve_generic_handler_by_given_interface() + public async Task Should_resolve_generic_handler() { var request = new GenericPing { Pong = new Pong { Message = "Ping" } }; var result = await _mediator.Send(request); @@ -281,7 +176,7 @@ public async Task Should_resolve_generic_handler_by_given_interface() } [Fact] - public async Task Should_resolve_generic_void_handler_by_given_interface() + public async Task Should_resolve_generic_void_handler() { var request = new VoidGenericPing(); await _mediator.Send(request);