Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MediatR.Examples.Windsor fix bug created by me before #744

Merged
merged 5 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 102 additions & 39 deletions samples/MediatR.Examples.Windsor/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace MediatR.Examples.Windsor;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using Castle.MicroKernel;
using Castle.MicroKernel.Registration;
using Castle.Windsor;

Expand All @@ -33,8 +35,9 @@ private static IMediator BuildMediator(WrappingWriter writer)
var fromAssemblyContainingPing = Classes.FromAssemblyContaining<Ping>();
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(Component.For(typeof(IPipelineBehavior<,>)).ImplementedBy(typeof(RequestExceptionActionProcessorBehavior<,>)));
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionAction<,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestExceptionHandler<,,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IStreamRequestHandler<,>)).WithServiceAllInterfaces().AllowMultipleMatches());
container.Register(fromAssemblyContainingPing.BasedOn(typeof(IRequestPreProcessor<>)).WithServiceAllInterfaces().AllowMultipleMatches());
Expand All @@ -53,45 +56,19 @@ private static IMediator BuildMediator(WrappingWriter writer)
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<ServiceFactory>();
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;
}

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);
// Handle exceptions even using the base request types for IRequestExceptionHandler<,,>
var isRequestExceptionHandler = service?.GetGenericTypeDefinition()
?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false;
if (isRequestExceptionHandler)
return ResolveRequestExceptionHandler(k, type, service, resolvedType, genericArguments);

// Handle exceptions even using the base request types for IRequestExceptionAction<,>
var isRequestExceptionAction = service?.GetGenericTypeDefinition()
?.IsAssignableTo(typeof(IRequestExceptionAction<,>)) ?? false;
if (isRequestExceptionAction)
return ResolveRequestExceptionAction(k, type, service, resolvedType, genericArguments);

return resultArray;
return resolvedType;
})));

//Pipeline
Expand All @@ -108,4 +85,90 @@ private static IMediator BuildMediator(WrappingWriter writer)

return mediator;
}

private static object ResolveRequestExceptionHandler(IKernel k, Type type, Type service, object resolvedType, Type[] genericArguments)
{
if (service == null
|| genericArguments == null
|| !service.IsInterface
|| !service.IsGenericType
|| !service.IsConstructedGenericType
|| !(service.GetGenericTypeDefinition()
?.IsAssignableTo(typeof(IRequestExceptionHandler<,,>)) ?? false)
|| genericArguments.Length != 3)
{
return resolvedType;
}

var serviceFactory = k.Resolve<ServiceFactory>();
var baseRequestType = genericArguments[0].BaseType;
var response = genericArguments[1];
var exceptionType = genericArguments[2];

// Check if the base request type is valid
if (baseRequestType == null
|| !baseRequestType.IsClass
|| baseRequestType == typeof(object)
|| ((!baseRequestType.GetInterfaces()
?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true))
{
return resolvedType;
}

var exceptionHandlerInterfaceType = typeof(IRequestExceptionHandler<,,>).MakeGenericType(baseRequestType, response, exceptionType);
var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType);
Array resultArray = CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(type, resolvedType, serviceFactory, enumerableExceptionHandlerInterfaceType);

return resultArray;
}

private static object ResolveRequestExceptionAction(IKernel k, Type type, Type service, object resolvedType, Type[] genericArguments)
{
if (service == null
|| genericArguments == null
|| !service.IsInterface
|| !service.IsGenericType
|| !service.IsConstructedGenericType
|| !(service.GetGenericTypeDefinition()
?.IsAssignableTo(typeof(IRequestExceptionAction<,>)) ?? false)
|| genericArguments.Length != 2)
{
return resolvedType;
}

var serviceFactory = k.Resolve<ServiceFactory>();
var baseRequestType = genericArguments[0].BaseType;
var exceptionType = genericArguments[1];

// Check if the base request type is valid
if (baseRequestType == null
|| !baseRequestType.IsClass
|| baseRequestType == typeof(object)
|| ((!baseRequestType.GetInterfaces()
?.Any(i => i.IsAssignableFrom(typeof(IRequest<>)))) ?? true))
{
return resolvedType;
}

var exceptionHandlerInterfaceType = typeof(IRequestExceptionAction<,>).MakeGenericType(baseRequestType, exceptionType);
var enumerableExceptionHandlerInterfaceType = typeof(IEnumerable<>).MakeGenericType(exceptionHandlerInterfaceType);
Array resultArray = CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(type, resolvedType, serviceFactory, enumerableExceptionHandlerInterfaceType);

return resultArray;
}

private static Array CreateArraysOutOfResolvedTypeAndEnumerableInterfaceTypes(Type type, object resolvedType, ServiceFactory serviceFactory, Type enumerableExceptionHandlerInterfaceType)
{
var firstArray = serviceFactory.Invoke(enumerableExceptionHandlerInterfaceType) as Array;
Debug.Assert(firstArray != null, $"Array '{nameof(firstArray)}' should not be null because this method calls ResolveAll when a {typeof(IEnumerable<>).FullName} " +
$"is passed as argument in argument named '{nameof(type)}'");

var secondArray = resolvedType is Array ? resolvedType as Array : new[] { resolvedType };
Debug.Assert(secondArray != null, $"Array '{nameof(secondArray)}' should not be null because '{nameof(resolvedType)}' is an array or created as an array");

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;
}
}
16 changes: 4 additions & 12 deletions samples/MediatR.Examples/ExceptionHandler/ExceptionsHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ protected override async Task Handle(PingResource request,
RequestExceptionHandlerState<Pong> state,
CancellationToken cancellationToken)
{
// Exception type name required because it is checked later in messages
await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}");

// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false);

Expand All @@ -38,9 +36,7 @@ public async Task Handle(PingResource request,
RequestExceptionHandlerState<Pong> state,
CancellationToken cancellationToken)
{
// Exception type name required because it is checked later in messages
await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}");

// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ConnectionExceptionHandler).FullName}'").ConfigureAwait(false);

Expand All @@ -59,9 +55,7 @@ public async Task Handle(PingResource request,
RequestExceptionHandlerState<Pong> state,
CancellationToken cancellationToken)
{
// Exception type name required because it is checked later in messages
await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}");

// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(AccessDeniedExceptionHandler).FullName}'").ConfigureAwait(false);

Expand All @@ -80,9 +74,7 @@ public virtual async Task Handle(PingNewResource request,
RequestExceptionHandlerState<Pong> state,
CancellationToken cancellationToken)
{
// Exception type name required because it is checked later in messages
await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}");

// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ protected override async Task Handle(PingResourceTimeout request,
RequestExceptionHandlerState<Pong> state,
CancellationToken cancellationToken)
{
// Exception type name required because it is checked later in messages
await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}");

// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(CommonExceptionHandler).FullName}'").ConfigureAwait(false);

Expand All @@ -38,9 +36,7 @@ public override async Task Handle(PingNewResource request,
RequestExceptionHandlerState<Pong> state,
CancellationToken cancellationToken)
{
// Exception type name required because it is checked later in messages
await _writer.WriteLineAsync($"Handling {exception.GetType().FullName}");

// Exception type name must be written in messages by LogExceptionAction before
// Exception handler type name required because it is checked later in messages
await _writer.WriteLineAsync($"---- Exception Handler: '{typeof(ServerExceptionHandler).FullName}'").ConfigureAwait(false);

Expand Down
4 changes: 3 additions & 1 deletion samples/MediatR.Examples/Runner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,10 @@ private static bool IsExceptionHandledBy<TException, THandler>(WrappingWriter wr
where TException : Exception
{
var messages = writer.Contents.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None).ToList();
if (messages.Count - 3 < 0)
return false;

// Note: For this handler type to be found in messages, it must be written in all tested exception handlers
// Note: For this handler type to be found in messages, it must be written in messages by LogExceptionAction
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);
Expand Down