Skip to content

Commit

Permalink
Merge branch 'master' into timeout-behavior-support
Browse files Browse the repository at this point in the history
  • Loading branch information
jbogard authored Sep 11, 2024
2 parents 83dbba0 + 69b54c8 commit 0d5ef88
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 33 deletions.
1 change: 0 additions & 1 deletion src/MediatR.Contracts/MediatR.Contracts.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<Description>Contracts package for requests, responses, and notifications</Description>
<Copyright>Copyright Jimmy Bogard</Copyright>
<TargetFramework>netstandard2.0</TargetFramework>
<Nullable>enable</Nullable>
<Features>strict</Features>
<PackageTags>mediator;request;response;queries;commands;notifications</PackageTags>
<SignAssembly>true</SignAssembly>
Expand Down
20 changes: 20 additions & 0 deletions src/MediatR/Entities/OpenBehavior.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using Microsoft.Extensions.DependencyInjection;

namespace MediatR.Entities;
/// <summary>
/// Creates open behavior entity.
/// </summary>
public class OpenBehavior
{
public OpenBehavior(Type openBehaviorType, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
{
OpenBehaviorType = openBehaviorType;
ServiceLifetime = serviceLifetime;
}

public Type? OpenBehaviorType { get; }
public ServiceLifetime ServiceLifetime { get; }


}
34 changes: 33 additions & 1 deletion src/MediatR/MicrosoftExtensionsDI/MediatrServiceConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Reflection;
using MediatR;
using MediatR.Entities;
using MediatR.NotificationPublishers;
using MediatR.Pipeline;
using MediatR.Registration;
Expand Down Expand Up @@ -92,7 +93,7 @@ public class MediatRServiceConfiguration
/// <summary>
/// Flag that controlls whether MediatR will attempt to register handlers that containg generic type parameters.
/// </summary>
public bool RegisterGenericHandlers { get; set; } = true;
public bool RegisterGenericHandlers { get; set; } = false;

/// <summary>
/// Register various handlers from assembly containing given type
Expand Down Expand Up @@ -222,6 +223,37 @@ public MediatRServiceConfiguration AddOpenBehavior(Type openBehaviorType, Servic
return this;
}

/// <summary>
/// Registers multiple open behavior types against the <see cref="IPipelineBehavior{TRequest,TResponse}"/> open generic interface type
/// </summary>
/// <param name="openBehaviorTypes">An open generic behavior type list includes multiple open generic behavior types.</param>
/// <param name="serviceLifetime">Optional service lifetime, defaults to <see cref="ServiceLifetime.Transient"/>.</param>
/// <returns>This</returns>
public MediatRServiceConfiguration AddOpenBehaviors(IEnumerable<Type> openBehaviorTypes, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
{
foreach (var openBehaviorType in openBehaviorTypes)
{
AddOpenBehavior(openBehaviorType, serviceLifetime);
}

return this;
}

/// <summary>
/// Registers open behaviors against the <see cref="IPipelineBehavior{TRequest,TResponse}"/> open generic interface type
/// </summary>
/// <param name="openBehaviors">An open generic behavior list includes multiple <see cref="OpenBehavior"/> open generic behaviors.</param>
/// <returns>This</returns>
public MediatRServiceConfiguration AddOpenBehaviors(IEnumerable<OpenBehavior> openBehaviors)
{
foreach (var openBehavior in openBehaviors)
{
AddOpenBehavior(openBehavior.OpenBehaviorType!, openBehavior.ServiceLifetime);
}

return this;
}

/// <summary>
/// Register a closed stream behavior type
/// </summary>
Expand Down
5 changes: 1 addition & 4 deletions src/MediatR/Registration/ServiceRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,7 @@ private static (Type Service, Type Implementation) GetConcreteRegistrationTypes(
var constraintsForEachParameter = openRequestHandlerImplementation
.GetGenericArguments()
.Select(x => x.GetGenericParameterConstraints())
.ToList();

if (constraintsForEachParameter.Count > 2 && constraintsForEachParameter.Any(constraints => !constraints.Where(x => x.IsInterface || x.IsClass).Any()))
throw new ArgumentException($"Error registering the generic handler type: {openRequestHandlerImplementation.FullName}. When registering generic requests with more than two type parameters, each type parameter must have at least one constraint of type interface or class.");
.ToList();

var typesThatCanCloseForEachParameter = constraintsForEachParameter
.Select(constraints => assembliesToScan
Expand Down
23 changes: 5 additions & 18 deletions test/MediatR.Tests/GenericRequestHandlerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public void ShouldResolveAllCombinationsOfGenericHandler(int numberOfClasses, in
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblies(dynamicAssembly);
cfg.RegisterGenericHandlers = true;
});

var provider = services.BuildServiceProvider();
Expand Down Expand Up @@ -93,24 +94,6 @@ public void ShouldNotRegisterDuplicateHandlers(int numberOfClasses, int numberOf
hasDuplicates.ShouldBeFalse();
}

[Fact]
public void ShouldThrowExceptionWhenRegisterningHandlersWithNoConstraints()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton(new Logger());

var assembly = GenerateMissingConstraintsAssembly();

Should.Throw<ArgumentException>(() =>
{
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(assembly);
});
})
.Message.ShouldContain("When registering generic requests with more than two type parameters, each type parameter must have at least one constraint of type interface or class.");
}

[Fact]
public void ShouldThrowExceptionWhenTypesClosingExceedsMaximum()
{
Expand All @@ -124,6 +107,7 @@ public void ShouldThrowExceptionWhenTypesClosingExceedsMaximum()
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(assembly);
cfg.RegisterGenericHandlers = true;
});
})
.Message.ShouldContain("One of the generic type parameter's count of types that can close exceeds the maximum length allowed");
Expand All @@ -142,6 +126,7 @@ public void ShouldThrowExceptionWhenGenericHandlerRegistrationsExceedsMaximum()
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(assembly);
cfg.RegisterGenericHandlers = true;
});
})
.Message.ShouldContain("The total number of generic type registrations exceeds the maximum allowed");
Expand All @@ -160,6 +145,7 @@ public void ShouldThrowExceptionWhenGenericTypeParametersExceedsMaximum()
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(assembly);
cfg.RegisterGenericHandlers = true;
});
})
.Message.ShouldContain("The number of generic type parameters exceeds the maximum allowed");
Expand All @@ -181,6 +167,7 @@ public void ShouldThrowExceptionWhenTimeoutOccurs()
cfg.MaxGenericTypeRegistrations = 0;
cfg.MaxTypesClosing = 0;
cfg.RegistrationTimeout = 1000;
cfg.RegisterGenericHandlers = true;
cfg.RegisterServicesFromAssembly(assembly);
});
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ public AssemblyResolutionTests()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton(new Logger());
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Ping).Assembly));
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(typeof(Ping).Assembly);
cfg.RegisterGenericHandlers = true;
});
_provider = services.BuildServiceProvider();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@
namespace MediatR.Tests.MicrosoftExtensionsDI
{
public abstract class BaseGenericRequestHandlerTests
{

protected static Assembly GenerateMissingConstraintsAssembly() =>
CreateAssemblyModuleBuilder("MissingConstraintsAssembly", 3, 3, CreateHandlerForMissingConstraintsTest);

{
protected static Assembly GenerateTypesClosingExceedsMaximumAssembly() =>
CreateAssemblyModuleBuilder("ExceedsMaximumTypesClosingAssembly", 201, 1, CreateHandlerForExceedsMaximumClassesTest);

Expand Down
16 changes: 13 additions & 3 deletions test/MediatR.Tests/SendTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public SendTests()
{
cfg.RegisterServicesFromAssemblies(typeof(Ping).Assembly);
cfg.AddOpenBehavior(typeof(TimeoutBehavior<,>), ServiceLifetime.Transient);
cfg.RegisterGenericHandlers = true;
});
services.AddSingleton(_dependency);
_serviceProvider = services.BuildServiceProvider();
Expand Down Expand Up @@ -303,8 +304,13 @@ public async Task Should_resolve_closed_handler_if_defined()
var dependency = new Dependency();
var services = new ServiceCollection();
services.AddSingleton(dependency);
services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly()));
services.AddTransient<IRequestHandler<VoidGenericPing<PongExtension>>, TestClass1PingRequestHandler>();
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly());
cfg.RegisterGenericHandlers = true;
});

services.AddTransient<IRequestHandler<VoidGenericPing<PongExtension>>,TestClass1PingRequestHandler>();
var serviceProvider = services.BuildServiceProvider();
var mediator = serviceProvider.GetService<IMediator>()!;

Expand All @@ -321,7 +327,11 @@ public async Task Should_resolve_open_handler_if_not_defined()
var dependency = new Dependency();
var services = new ServiceCollection();
services.AddSingleton(dependency);
services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly()));
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssemblies(Assembly.GetExecutingAssembly());
cfg.RegisterGenericHandlers = true;
});
services.AddTransient<IRequestHandler<VoidGenericPing<PongExtension>>, TestClass1PingRequestHandler>();
var serviceProvider = services.BuildServiceProvider();
var mediator = serviceProvider.GetService<IMediator>()!;
Expand Down

0 comments on commit 0d5ef88

Please sign in to comment.