diff --git a/samples/MediatR.Examples.Windsor/Program.cs b/samples/MediatR.Examples.Windsor/Program.cs index 84e081c8..063b58b9 100644 --- a/samples/MediatR.Examples.Windsor/Program.cs +++ b/samples/MediatR.Examples.Windsor/Program.cs @@ -18,7 +18,7 @@ private static Task Main(string[] args) var writer = new WrappingWriter(Console.Out); var mediator = BuildMediator(writer); - return Runner.Run(mediator, writer, "Castle.Windsor"); + return Runner.Run(mediator, writer, "Castle.Windsor", true); } private static IMediator BuildMediator(WrappingWriter writer) @@ -29,9 +29,16 @@ private static IMediator BuildMediator(WrappingWriter writer) // *** The default lifestyle for Windsor is Singleton // *** If you are using ASP.net, it's better to register your services with 'Per Web Request LifeStyle'. - - container.Register(Classes.FromAssemblyContaining().BasedOn(typeof(IRequestHandler<,>)).WithServiceAllInterfaces()); - container.Register(Classes.FromAssemblyContaining().BasedOn(typeof(INotificationHandler<>)).WithServiceAllInterfaces()); + + var fromAssemblyContainingPing = Classes.FromAssemblyContaining(); + container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); + container.Register(fromAssemblyContainingPing.BasedOn(typeof(INotificationHandler<>)).WithServiceAllInterfaces().AllowMultipleMatches()); + container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionActionProcessorBehavior<,>))); + container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionProcessorBehavior<,>))); + container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionHandler<,,>)).WithServiceAllInterfaces().AllowMultipleMatches()); + container.Register(fromAssemblyContainingPing.BasedOn(typeof(IStreamRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); + container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPreProcessor<>)).WithServiceAllInterfaces().AllowMultipleMatches()); + container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPostProcessor<,>)).WithServiceAllInterfaces().AllowMultipleMatches()); container.Register(Component.For().ImplementedBy()); container.Register(Component.For().Instance(writer)); @@ -39,18 +46,61 @@ private static IMediator BuildMediator(WrappingWriter writer) { var enumerableType = type .GetInterfaces() - .Concat(new [] {type}) - .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); + .Concat(new[] { type }) + .FirstOrDefault(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>)); + + var service = enumerableType?.GetGenericArguments()?[0]; + var resolvedType = enumerableType != null ? k.ResolveAll(service) : k.Resolve(type); + var genericArguments = service?.GetGenericArguments(); + + // Handle exceptions even using the base request types + if (service == null + || genericArguments == null + || !service.IsInterface + || !service.IsGenericType + || !service.IsConstructedGenericType + || !(service.GetGenericTypeDefinition() + ?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false) + || genericArguments.Length != 3 + || !(genericArguments[0].BaseType?.IsClass ?? false)) + { + return resolvedType; + } + + var serviceFactory = k.Resolve(); + var baseRequestType = genericArguments[0].BaseType; + var response = genericArguments[1]; + var exceptionType = genericArguments[2]; + + // Check if the base request type is valid + if (!baseRequestType.IsClass + || baseRequestType == typeof(object) + || ((!baseRequestType.GetInterfaces() + ?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true)) + { + return resolvedType; + } - return enumerableType != null ? k.ResolveAll(enumerableType.GetGenericArguments()[0]) : k.Resolve(type); + var exceptionHandlerInterfaceType = typeof(IRequestExceptionHandler<,,>).MakeGenericType(baseRequestType, response, exceptionType); + var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType); + + // This is assumed Array because this method calls ResolveAll when a IEnumerable<> is passed as argument + var firstArray = serviceFactory.Invoke(enumerableExceptionHandlerInterfaceType) as Array; + var secondArray = resolvedType is Array ? resolvedType as Array : new[] { resolvedType }; + var resultArray = Array.CreateInstance(typeof(object), firstArray.Length + secondArray.Length); + Array.Copy(firstArray, resultArray, firstArray.Length); + Array.Copy(secondArray, 0, resultArray, firstArray.Length, secondArray.Length); + + return resultArray; }))); //Pipeline + container.Register(Component.For(typeof(IStreamPipelineBehavior<,>)).ImplementedBy(typeof(GenericStreamPipelineBehavior<,>))); container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestPreProcessorBehavior<,>)).NamedAutomatically("PreProcessorBehavior")); container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestPostProcessorBehavior<,>)).NamedAutomatically("PostProcessorBehavior")); container.Register(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(GenericPipelineBehavior<,>)).NamedAutomatically("Pipeline")); container.Register(Component.For(typeof(IRequestPreProcessor<>)).ImplementedBy(typeof(GenericRequestPreProcessor<>)).NamedAutomatically("PreProcessor")); - container.Register(Component.For(typeof(IRequestPostProcessor <,>)).ImplementedBy(typeof(GenericRequestPostProcessor<,>)).NamedAutomatically("PostProcessor")); + container.Register(Component.For(typeof(IRequestPostProcessor<,>)).ImplementedBy(typeof(GenericRequestPostProcessor<,>)).NamedAutomatically("PostProcessor")); container.Register(Component.For(typeof(IRequestPostProcessor<,>), typeof(ConstrainedRequestPostProcessor<,>)).NamedAutomatically("ConstrainedRequestPostProcessor")); container.Register(Component.For(typeof(INotificationHandler<>), typeof(ConstrainedPingedHandler<>)).NamedAutomatically("ConstrainedPingedHandler")); @@ -58,4 +108,4 @@ private static IMediator BuildMediator(WrappingWriter writer) return mediator; } -} \ No newline at end of file +} diff --git a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs b/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs index 77b9b47e..29f3a310 100644 --- a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs +++ b/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs @@ -17,7 +17,12 @@ protected override async Task Handle(PingResource request, RequestExceptionHandlerState state, CancellationToken cancellationToken) { + // Exception type name required because it is checked later in messages + await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}"); + + // Exception handler type name required because it is checked later in messages await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false); + state.SetHandled(new Pong()); } } @@ -33,7 +38,12 @@ public async Task Handle(PingResource request, RequestExceptionHandlerState state, CancellationToken cancellationToken) { + // Exception type name required because it is checked later in messages + await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}"); + + // Exception handler type name required because it is checked later in messages await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ConnectionExceptionHandler).FullName}'").ConfigureAwait(false); + state.SetHandled(new Pong()); } } @@ -49,7 +59,12 @@ public async Task Handle(PingResource request, RequestExceptionHandlerState state, CancellationToken cancellationToken) { + // Exception type name required because it is checked later in messages + await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}"); + + // Exception handler type name required because it is checked later in messages await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(AccessDeniedExceptionHandler).FullName}'").ConfigureAwait(false); + state.SetHandled(new Pong()); } } @@ -65,7 +80,12 @@ public virtual async Task Handle(PingNewResource request, RequestExceptionHandlerState state, CancellationToken cancellationToken) { + // Exception type name required because it is checked later in messages + await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}"); + + // Exception handler type name required because it is checked later in messages await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false); + state.SetHandled(new Pong()); } -} \ No newline at end of file +} diff --git a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs b/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs index 0d2c2b12..b65d0ab5 100644 --- a/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs +++ b/samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlersOverrides.cs @@ -17,7 +17,12 @@ protected override async Task Handle(PingResourceTimeout request, RequestExceptionHandlerState state, CancellationToken cancellationToken) { + // Exception type name required because it is checked later in messages + await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}"); + + // Exception handler type name required because it is checked later in messages await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false); + state.SetHandled(new Pong()); } } @@ -33,7 +38,12 @@ public override async Task Handle(PingNewResource request, RequestExceptionHandlerState state, CancellationToken cancellationToken) { + // Exception type name required because it is checked later in messages + await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}"); + + // Exception handler type name required because it is checked later in messages await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false); + state.SetHandled(new Pong()); } -} \ No newline at end of file +} diff --git a/samples/MediatR.Examples/Runner.cs b/samples/MediatR.Examples/Runner.cs index b3f293c1..d907b849 100644 --- a/samples/MediatR.Examples/Runner.cs +++ b/samples/MediatR.Examples/Runner.cs @@ -281,7 +281,9 @@ private static bool IsExceptionHandledBy(WrappingWriter wr { var messages = writer.Contents.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList(); + // Note: For this handler type to be found in messages, it must be written in all tested exception handlers return messages[messages.Count - 2].Contains(typeof(THandler).FullName) + // Note: For this exception type to be found in messages, exception must be written in all tested exception handlers && messages[messages.Count - 3].Contains(typeof(TException).FullName); } } @@ -336,4 +338,4 @@ public override Task WriteLineAsync(string value) public override Encoding Encoding => _innerWriter.Encoding; public string Contents => _stringWriter.ToString(); -} \ No newline at end of file +}