diff --git a/src/Cortex.Mediator/Cortex.Mediator.csproj b/src/Cortex.Mediator/Cortex.Mediator.csproj index b9e165b..e0e6275 100644 --- a/src/Cortex.Mediator/Cortex.Mediator.csproj +++ b/src/Cortex.Mediator/Cortex.Mediator.csproj @@ -67,6 +67,5 @@ - diff --git a/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs b/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs index 4ebde1c..60e0c06 100644 --- a/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Cortex.Mediator/DependencyInjection/ServiceCollectionExtensions.cs @@ -7,8 +7,8 @@ using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; -using System.Data; using System.Linq; +using System.Reflection; namespace Cortex.Mediator.DependencyInjection { @@ -43,43 +43,17 @@ private static void RegisterHandlers( var assemblies = assemblyMarkerTypes.Select(t => t.Assembly).ToArray(); var lifetime = options.HandlerLifetime; - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(ICommandHandler<,>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithLifetime(lifetime)); - + ScanAndRegister(services, assemblies, typeof(ICommandHandler<,>), options.OnlyPublicClasses, lifetime); // feature #141 - Register void command handlers - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(ICommandHandler<>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithLifetime(lifetime)); - - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(IQueryHandler<,>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithLifetime(lifetime)); - - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(INotificationHandler<>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithLifetime(lifetime)); + ScanAndRegister(services, assemblies, typeof(ICommandHandler<>), options.OnlyPublicClasses, lifetime); + + ScanAndRegister(services, assemblies, typeof(IQueryHandler<,>), options.OnlyPublicClasses, lifetime); + + ScanAndRegister(services, assemblies, typeof(INotificationHandler<>), options.OnlyPublicClasses, lifetime); // Register streaming query handlers - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(IStreamQueryHandler<,>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithLifetime(lifetime)); + ScanAndRegister(services, assemblies, typeof(IStreamQueryHandler<,>), options.OnlyPublicClasses, lifetime); } private static void RegisterProcessors( @@ -90,28 +64,51 @@ private static void RegisterProcessors( var assemblies = assemblyMarkerTypes.Select(t => t.Assembly).ToArray(); // Register pre-processors - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(IRequestPreProcessor<>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithTransientLifetime()); + ScanAndRegister(services, assemblies, typeof(IRequestPreProcessor<>), options.OnlyPublicClasses, ServiceLifetime.Transient); // Register post-processors with response - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(IRequestPostProcessor<,>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithTransientLifetime()); + ScanAndRegister(services, assemblies, typeof(IRequestPostProcessor<,>), options.OnlyPublicClasses, ServiceLifetime.Transient); // Register post-processors without response (for void commands) - services.Scan(scan => scan - .FromAssemblies(assemblies) - .AddClasses(classes => classes - .AssignableTo(typeof(IRequestPostProcessor<>)), options.OnlyPublicClasses) - .AsImplementedInterfaces() - .WithTransientLifetime()); + ScanAndRegister(services, assemblies, typeof(IRequestPostProcessor<>), options.OnlyPublicClasses, ServiceLifetime.Transient); + } + + private static void ScanAndRegister( + IServiceCollection services, + IEnumerable assemblies, + Type openGenericInterface, + bool onlyPublicClasses, + ServiceLifetime lifetime) + { + foreach (var assembly in assemblies) + { + Type[] types; + try + { + types = onlyPublicClasses + ? assembly.GetExportedTypes() + : assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + types = ex.Types.Where(t => t != null).ToArray(); + } + + foreach (var type in types) + { + if (type.IsInterface || type.IsAbstract || type.IsGenericTypeDefinition) + continue; + + var matchingInterfaces = type.GetInterfaces() + .Where(i => i.IsGenericType && + i.GetGenericTypeDefinition() == openGenericInterface); + + foreach (var serviceType in matchingInterfaces) + { + services.Add(new ServiceDescriptor(serviceType, type, lifetime)); + } + } + } } private static void RegisterPipelineBehaviors(IServiceCollection services, MediatorOptions options) diff --git a/src/Cortex.Tests/Mediator/Tests/HandlerLifetimeTests.cs b/src/Cortex.Tests/Mediator/Tests/HandlerLifetimeTests.cs index 947f8c2..47eae2d 100644 --- a/src/Cortex.Tests/Mediator/Tests/HandlerLifetimeTests.cs +++ b/src/Cortex.Tests/Mediator/Tests/HandlerLifetimeTests.cs @@ -10,7 +10,7 @@ namespace Cortex.Tests.Mediator.Tests { #region Test Types for Handler Lifetime Tests - // These types live in this assembly so Scrutor can discover them via the marker type. + // These types live in this assembly so assembly scanning can discover them via the marker type. public class LifetimeTestCommand : ICommand {