jbogard / MediatR

Simple, unambitious mediator implementation in .NET
Apache License 2.0
11.14k stars 1.18k forks source link

Consolidate registration to single configuration object and optimize registration #829

Closed jbogard closed 1 year ago

jbogard commented 1 year ago

The overloads for the ServiceCollection extensions are too many to maintain, this proposes to consolidate the configuration API in to the MediatRServiceConfiguration object.

API changes

All configuration would be consolidated into a single method (instead of parameters in an overload):

public static class ServiceCollectionExtensions {
-    public static IServiceCollection AddMediatR(this IServiceCollection services, params Assembly[] assemblies)
-    public static IServiceCollection AddMediatR(this IServiceCollection services, Action<MediatRServiceConfiguration>? configuration, params Assembly[] assemblies)
-    public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable<Assembly> assemblies, Action<MediatRServiceConfiguration>? configuration)
-    public static IServiceCollection AddMediatR(this IServiceCollection services, params Type[] handlerAssemblyMarkerTypes)
-    public static IServiceCollection AddMediatR(this IServiceCollection services, Action<MediatRServiceConfiguration>? configuration, params Type[] handlerAssemblyMarkerTypes)
-    public static IServiceCollection AddMediatR(this IServiceCollection services, IEnumerable<Type> handlerAssemblyMarkerTypes, Action<MediatRServiceConfiguration>? configuration)
+    public static IServiceCollection AddMediatR(this IServiceCollection services, Action<MediatRServiceConfiguration> configuration)
}

All the parameters move to the MediatRServiceConfiguration object, including a pass-through method to add behaviors:

public class MediatRServiceConfiguration {
-    public Func<Type, bool> TypeEvaluator { get; private set; } = t => true;
-    public Type MediatorImplementationType { get; private set; }
-    public ServiceLifetime Lifetime { get; private set; }
+    public Func<Type, bool> TypeEvaluator { get; set; } = t => true;
+    public Type MediatorImplementationType { get; set; } = typeof(Mediator);
+    public ServiceLifetime Lifetime { get; set; } = ServiceLifetime.Transient;
+    public RequestExceptionActionProcessorStrategy RequestExceptionActionProcessorStrategy { get; set; } = RequestExceptionActionProcessorStrategy.ApplyForUnhandledExceptions;
+    internal List<Assembly> AssembliesToRegister { get; } = new();
+    public List<ServiceDescriptor> BehaviorsToRegister { get; } = new();

// These methods removed to instead use the properties directly
-    public MediatRServiceConfiguration Using<TMediator>() where TMediator : IMediator
-    public MediatRServiceConfiguration AsSingleton()
-    public MediatRServiceConfiguration AsScoped()
-    public MediatRServiceConfiguration AsTransient()
-    public MediatRServiceConfiguration WithEvaluator(Func<Type, bool> evaluator)

// These replace the overloads for passing in types or assemblies to scan
+    public MediatRServiceConfiguration RegisterServicesFromAssemblyContaining<T>()
+    public MediatRServiceConfiguration RegisterServicesFromAssemblyContaining(Type type)
+    public MediatRServiceConfiguration RegisterServicesFromAssembly(Assembly assembly)
+    public MediatRServiceConfiguration RegisterServicesFromAssemblies(params Assembly[] assemblies)

// These are new methods to add concrete behaviors but merely passthrough the configuration to the underlying `IServiceCollection`
+    public MediatRServiceConfiguration AddBehavior<TServiceType, TImplementationType>(ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
+    public MediatRServiceConfiguration AddBehavior(Type serviceType, Type implementationType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
// This method is for registering an open behavior still passing through the configuration
+    public MediatRServiceConfiguration AddOpenBehavior(Type openBehaviorType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)

Behavior changes

The service scanning modified to only register with the container the "built-in" behaviors of:

When "Sub-behaviors" of those types are found in the IServiceCollection.

There are performance implications to resolving those sub-behaviors, so if they don't exist, the built-in behavior won't be registered.

jbogard commented 1 year ago

Fixed by #828