diff --git a/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.Factories.cs b/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.Factories.cs new file mode 100644 index 0000000..744c35c --- /dev/null +++ b/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.Factories.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; + +namespace Xer.Cqrs.CommandStack.Extensions.Attributes +{ + public partial class CommandHandlerAttributeMethod + { + #region Factory Methods + + /// + /// Create CommandHandlerAttributeMethod from the method info. + /// + /// Method info that has CommandHandlerAttribute custom attribute. + /// Factory delegate that provides an instance of the method info's declaring type. + /// Instance of CommandHandlerAttributeMethod. + public static CommandHandlerAttributeMethod FromMethodInfo(MethodInfo methodInfo, Func instanceFactory) + { + Type commandType; + bool isAsyncMethod; + + if (methodInfo == null) + { + throw new ArgumentNullException(nameof(methodInfo)); + } + + if (!IsValid(methodInfo)) + { + throw new InvalidOperationException($"Method is not marked with [CommandHandler] attribute. {createCheckMethodMessage(methodInfo)}."); + } + + // Get all method parameters. + ParameterInfo[] methodParameters = methodInfo.GetParameters(); + + // Get first method parameter that is a class (not struct). This assumes that the first parameter is the command. + ParameterInfo commandParameter = methodParameters.FirstOrDefault(); + if (commandParameter != null) + { + // Check if parameter is a class. + if (!commandParameter.ParameterType.GetTypeInfo().IsClass) + { + throw new InvalidOperationException($"Method's command parameter is not a reference type, only reference type commands are supported. {createCheckMethodMessage(methodInfo)}."); + } + + // Set command type. + commandType = commandParameter.ParameterType; + } + else + { + // Method has no parameter. + throw new InvalidOperationException($"Method must accept a command object as a parameter. {createCheckMethodMessage(methodInfo)}."); + } + + // Only valid return types are Task/void. + if (methodInfo.ReturnType == typeof(Task)) + { + isAsyncMethod = true; + } + else if (methodInfo.ReturnType == typeof(void)) + { + isAsyncMethod = false; + + // if(methodInfo.CustomAttributes.Any(p => p.AttributeType == typeof(AsyncStateMachineAttribute))) + // { + // throw new InvalidOperationException($"Methods with async void signatures are not allowed. A Task may be used as return type instead of void. Check method: {methodInfo.ToString()}."); + // } + } + else + { + // Return type is not Task/void. Invalid. + throw new InvalidOperationException($"Method marked with [CommandHandler] can only have void or a Task as return value. {createCheckMethodMessage(methodInfo)}."); + } + + bool supportsCancellation = methodParameters.Any(p => p.ParameterType == typeof(CancellationToken)); + + if (!isAsyncMethod && supportsCancellation) + { + throw new InvalidOperationException($"Cancellation token support is only available for async methods (methods returning a Task). {createCheckMethodMessage(methodInfo)}."); + } + + return new CommandHandlerAttributeMethod(methodInfo, commandType, instanceFactory, isAsyncMethod, supportsCancellation); + + // Local function. + string createCheckMethodMessage(MethodInfo method) => $"Check {methodInfo.DeclaringType.Name}'s {methodInfo.ToString()} method"; + } + + /// + /// Create CommandHandlerAttributeMethod from the method info. + /// + /// Method infos that have CommandHandlerAttribute custom attributes. + /// Factory delegate that provides an instance of a method info's declaring type. + /// Instances of CommandHandlerAttributeMethod. + public static IEnumerable FromMethodInfos(IEnumerable methodInfos, Func instanceFactory) + { + if (methodInfos == null) + { + throw new ArgumentNullException(nameof(methodInfos)); + } + + return methodInfos.Select(m => FromMethodInfo(m, () => instanceFactory.Invoke(m.DeclaringType))); + } + + /// + /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. + /// + /// Type to scan for methods marked with the [CommandHandler] attribute. + /// Factory delegate that provides an instance of the specified type. + /// List of all CommandHandlerAttributeMethod detected. + public static IEnumerable FromType(Func instanceFactory) where T : class + { + return FromType(typeof(T), instanceFactory); + } + + /// + /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. + /// + /// Type to scan for methods marked with the [CommandHandler] attribute. + /// Factory delegate that provides an instance of the specified type. + /// List of all CommandHandlerAttributeMethod detected. + public static IEnumerable FromType(Type type, Func instanceFactory) + { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + IEnumerable methods = type.GetTypeInfo().DeclaredMethods.Where(m => IsValid(m)); + + return FromMethodInfos(methods, _ => instanceFactory.Invoke()); + } + + /// + /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. + /// + /// Types to scan for methods marked with the [CommandHandler] attribute. + /// Factory delegate that provides an instance of a given type. + /// List of all CommandHandlerAttributeMethod detected. + public static IEnumerable FromTypes(IEnumerable types, Func instanceFactory) + { + if (types == null) + { + throw new ArgumentNullException(nameof(types)); + } + + return types.SelectMany(type => FromType(type, () => instanceFactory.Invoke(type))); + } + + /// + /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. + /// + /// Assembly to scan for methods marked with the [CommandHandler] attribute. + /// Factory delegate that provides an instance of a type that has methods marked with [CommandHandler] attribute. + /// List of all CommandHandlerAttributeMethod detected. + public static IEnumerable FromAssembly(Assembly commandHandlerAssembly, Func instanceFactory) + { + if (commandHandlerAssembly == null) + { + throw new ArgumentNullException(nameof(commandHandlerAssembly)); + } + + IEnumerable commandHandlerMethods = commandHandlerAssembly.DefinedTypes + .Where(typeInfo => IsFoundInType(typeInfo)) + .SelectMany(typeInfo => typeInfo.DeclaredMethods.Where(method => + IsValid(method))); + + return FromMethodInfos(commandHandlerMethods, instanceFactory); + } + + /// + /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. + /// + /// Assemblies to scan for methods marked with the [CommandHandler] attribute. + /// Factory delegate that provides an instance of a type that has methods marked with [CommandHandler] attribute. + /// List of all CommandHandlerAttributeMethod detected. + public static IEnumerable FromAssemblies(IEnumerable commandHandlerAssemblies, Func instanceFactory) + { + if (commandHandlerAssemblies == null) + { + throw new ArgumentNullException(nameof(commandHandlerAssemblies)); + } + + return commandHandlerAssemblies.SelectMany(assembly => FromAssembly(assembly, instanceFactory)); + } + + #endregion Factory Methods + } +} \ No newline at end of file diff --git a/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.cs b/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.cs index 72716cd..c693e6c 100644 --- a/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.cs +++ b/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerAttributeMethod.cs @@ -1,11 +1,10 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Threading; using System.Threading.Tasks; -using Xer.Cqrs.CommandStack; +using Xer.Delegator; namespace Xer.Cqrs.CommandStack.Extensions.Attributes { @@ -16,14 +15,14 @@ namespace Xer.Cqrs.CommandStack.Extensions.Attributes /// - Task HandleCommandAsync(TCommand command); /// - Task HandleCommandAsync(TCommand command, CancellationToken cancellationToken); /// - public class CommandHandlerAttributeMethod + public partial class CommandHandlerAttributeMethod { #region Static Declarations private static readonly ParameterExpression CancellationTokenParameterExpression = Expression.Parameter(typeof(CancellationToken), "cancellationToken"); - private static readonly MethodInfo CreateWrappedSyncDelegateOpenGenericMethodInfo = typeof(CommandHandlerAttributeMethod).GetTypeInfo().GetDeclaredMethod(nameof(createWrappedSyncDelegate)); - private static readonly MethodInfo CreateCancellableAsyncDelegateOpenGenericMethodInfo = typeof(CommandHandlerAttributeMethod).GetTypeInfo().GetDeclaredMethod(nameof(createCancellableAsyncDelegate)); - private static readonly MethodInfo CreateNonCancellableAsyncDelegateOpenGenericMethodInfo = typeof(CommandHandlerAttributeMethod).GetTypeInfo().GetDeclaredMethod(nameof(createNonCancellableAsyncDelegate)); + private static readonly MethodInfo BuildWrappedSyncDelegateOpenGenericMethodInfo = typeof(CommandHandlerAttributeMethod).GetTypeInfo().GetDeclaredMethod(nameof(BuildWrappedSyncDelegate)); + private static readonly MethodInfo BuildCancellableAsyncDelegateOpenGenericMethodInfo = typeof(CommandHandlerAttributeMethod).GetTypeInfo().GetDeclaredMethod(nameof(BuildCancellableAsyncDelegate)); + private static readonly MethodInfo BuildNonCancellableAsyncDelegateOpenGenericMethodInfo = typeof(CommandHandlerAttributeMethod).GetTypeInfo().GetDeclaredMethod(nameof(BuildNonCancellableAsyncDelegate)); #endregion Static Declarations @@ -91,7 +90,7 @@ private CommandHandlerAttributeMethod(MethodInfo methodInfo, Type commandType, F /// Create a delegate that handles a command that is specified in . /// /// Delegate that handles a command that is specified in . - public Func CreateCommandHandlerDelegate() + public MessageHandlerDelegate CreateCommandHandlerDelegate() { try { @@ -99,207 +98,27 @@ public Func CreateCommandHandlerDelegate() { if (SupportsCancellation) { - // Invoke createCancellableAsyncDelegate(attributedObjectFactory) - return (Func)CreateCancellableAsyncDelegateOpenGenericMethodInfo - .MakeGenericMethod(DeclaringType, CommandType) - .Invoke(this, new object[] { InstanceFactory }); + // Invoke BuildCancellableAsyncDelegate(attributedObjectFactory) + return InvokeDelegateBuilderMethod(BuildCancellableAsyncDelegateOpenGenericMethodInfo); } else { - // Invoke createNonCancellableAsyncDelegate(attributedObjectFactory) - return (Func)CreateNonCancellableAsyncDelegateOpenGenericMethodInfo - .MakeGenericMethod(DeclaringType,CommandType) - .Invoke(this, new object[] { InstanceFactory }); + // Invoke BuildNonCancellableAsyncDelegate(attributedObjectFactory) + return InvokeDelegateBuilderMethod(BuildNonCancellableAsyncDelegateOpenGenericMethodInfo); } } else { - // Invoke createWrappedSyncDelegate(attributedObjectFactory) - return (Func)CreateWrappedSyncDelegateOpenGenericMethodInfo - .MakeGenericMethod(DeclaringType, CommandType) - .Invoke(this, new object[] { InstanceFactory }); + // Invoke BuildWrappedSyncDelegate(attributedObjectFactory) + return InvokeDelegateBuilderMethod(BuildWrappedSyncDelegateOpenGenericMethodInfo); } } catch (Exception ex) { throw new InvalidOperationException($"Failed to create command handler delegate for {DeclaringType.Name}'s {MethodInfo.ToString()} method.", ex); } - } - - /// - /// Create CommandHandlerAttributeMethod from the method info. - /// - /// Method info that has CommandHandlerAttribute custom attribute. - /// Factory delegate that provides an instance of the method info's declaring type. - /// Instance of CommandHandlerAttributeMethod. - public static CommandHandlerAttributeMethod FromMethodInfo(MethodInfo methodInfo, Func instanceFactory) - { - Type commandType; - bool isAsyncMethod; - - if (methodInfo == null) - { - throw new ArgumentNullException(nameof(methodInfo)); - } - - if (!IsValid(methodInfo)) - { - throw new InvalidOperationException($"Method is not marked with [CommandHandler] attribute. {createCheckMethodMessage(methodInfo)}."); - } - - // Get all method parameters. - ParameterInfo[] methodParameters = methodInfo.GetParameters(); - - // Get first method parameter that is a class (not struct). This assumes that the first parameter is the command. - ParameterInfo commandParameter = methodParameters.FirstOrDefault(); - if (commandParameter != null) - { - // Check if parameter is a class. - if (!commandParameter.ParameterType.GetTypeInfo().IsClass) - { - throw new InvalidOperationException($"Method's command parameter is not a reference type, only reference type commands are supported. {createCheckMethodMessage(methodInfo)}."); - } - - // Set command type. - commandType = commandParameter.ParameterType; - } - else - { - // Method has no parameter. - throw new InvalidOperationException($"Method must accept a command object as a parameter. {createCheckMethodMessage(methodInfo)}."); - } - - // Only valid return types are Task/void. - if (methodInfo.ReturnType == typeof(Task)) - { - isAsyncMethod = true; - } - else if (methodInfo.ReturnType == typeof(void)) - { - isAsyncMethod = false; - - // if(methodInfo.CustomAttributes.Any(p => p.AttributeType == typeof(AsyncStateMachineAttribute))) - // { - // throw new InvalidOperationException($"Methods with async void signatures are not allowed. A Task may be used as return type instead of void. Check method: {methodInfo.ToString()}."); - // } - } - else - { - // Return type is not Task/void. Invalid. - throw new InvalidOperationException($"Method marked with [CommandHandler] can only have void or a Task as return value. {createCheckMethodMessage(methodInfo)}."); - } - - bool supportsCancellation = methodParameters.Any(p => p.ParameterType == typeof(CancellationToken)); - - if (!isAsyncMethod && supportsCancellation) - { - throw new InvalidOperationException($"Cancellation token support is only available for async methods (methods returning a Task). {createCheckMethodMessage(methodInfo)}."); - } - - return new CommandHandlerAttributeMethod(methodInfo, commandType, instanceFactory, isAsyncMethod, supportsCancellation); - - // Local function. - string createCheckMethodMessage(MethodInfo method) => $"Check {methodInfo.DeclaringType.Name}'s {methodInfo.ToString()} method"; - } - - /// - /// Create CommandHandlerAttributeMethod from the method info. - /// - /// Method infos that have CommandHandlerAttribute custom attributes. - /// Factory delegate that provides an instance of a method info's declaring type. - /// Instances of CommandHandlerAttributeMethod. - public static IEnumerable FromMethodInfos(IEnumerable methodInfos, Func instanceFactory) - { - if (methodInfos == null) - { - throw new ArgumentNullException(nameof(methodInfos)); - } - - return methodInfos.Select(m => FromMethodInfo(m, () => instanceFactory.Invoke(m.DeclaringType))); - } - - /// - /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. - /// - /// Type to scan for methods marked with the [CommandHandler] attribute. - /// Factory delegate that provides an instance of the specified type. - /// List of all CommandHandlerAttributeMethod detected. - public static IEnumerable FromType(Func instanceFactory) where T : class - { - return FromType(typeof(T), instanceFactory); - } - - /// - /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. - /// - /// Type to scan for methods marked with the [CommandHandler] attribute. - /// Factory delegate that provides an instance of the specified type. - /// List of all CommandHandlerAttributeMethod detected. - public static IEnumerable FromType(Type type, Func instanceFactory) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - IEnumerable methods = type.GetTypeInfo().DeclaredMethods.Where(m => IsValid(m)); - - return FromMethodInfos(methods, _ => instanceFactory.Invoke()); - } - - /// - /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. - /// - /// Types to scan for methods marked with the [CommandHandler] attribute. - /// Factory delegate that provides an instance of a given type. - /// List of all CommandHandlerAttributeMethod detected. - public static IEnumerable FromTypes(IEnumerable types, Func instanceFactory) - { - if (types == null) - { - throw new ArgumentNullException(nameof(types)); - } - - return types.SelectMany(type => FromType(type, () => instanceFactory.Invoke(type))); - } - - /// - /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. - /// - /// Assembly to scan for methods marked with the [CommandHandler] attribute. - /// Factory delegate that provides an instance of a type that has methods marked with [CommandHandler] attribute. - /// List of all CommandHandlerAttributeMethod detected. - public static IEnumerable FromAssembly(Assembly commandHandlerAssembly, Func instanceFactory) - { - if (commandHandlerAssembly == null) - { - throw new ArgumentNullException(nameof(commandHandlerAssembly)); - } - - IEnumerable commandHandlerMethods = commandHandlerAssembly.DefinedTypes - .Where(typeInfo => IsFoundInType(typeInfo)) - .SelectMany(typeInfo => typeInfo.DeclaredMethods.Where(method => - IsValid(method))); - - return FromMethodInfos(commandHandlerMethods, instanceFactory); - } - - /// - /// Detect methods marked with [CommandHandler] attribute and translate to CommandHandlerAttributeMethod instances. - /// - /// Assemblies to scan for methods marked with the [CommandHandler] attribute. - /// Factory delegate that provides an instance of a type that has methods marked with [CommandHandler] attribute. - /// List of all CommandHandlerAttributeMethod detected. - public static IEnumerable FromAssemblies(IEnumerable commandHandlerAssemblies, Func instanceFactory) - { - if (commandHandlerAssemblies == null) - { - throw new ArgumentNullException(nameof(commandHandlerAssemblies)); - } - - return commandHandlerAssemblies.SelectMany(assembly => FromAssembly(assembly, instanceFactory)); - } + } /// /// Check if a method marked with [CommandHandler] attribute is found in the specified type. @@ -352,35 +171,25 @@ public static bool IsValid(MethodInfo methodInfo) /// Type of command that is handled by the CommandHandlerAttributeMethod. This should match CommandType property. /// Factory delegate which produces an instance of a given type. /// Delegate that handles a command. - private Func createCancellableAsyncDelegate(Func attributedObjectFactory) + private MessageHandlerDelegate BuildCancellableAsyncDelegate(Func attributedObjectFactory) where TAttributed : class where TCommand : class { // Create an expression that will invoke the command handler method of a given instance. - var instanceParameterExpression = Expression.Parameter(typeof(TAttributed), "instance"); - var commandParameterExpression = Expression.Parameter(typeof(TCommand), "command"); - var callExpression = Expression.Call(instanceParameterExpression, MethodInfo, commandParameterExpression, CancellationTokenParameterExpression); + ParameterExpression instanceParameterExpression = Expression.Parameter(typeof(TAttributed), "instance"); + ParameterExpression commandParameterExpression = Expression.Parameter(typeof(TCommand), "command"); + MethodCallExpression callExpression = Expression.Call(instanceParameterExpression, MethodInfo, commandParameterExpression, CancellationTokenParameterExpression); // Lambda signature: // (instance, command, cancallationToken) => instance.HandleCommandAsync(command, cancellationToken); - var cancellableAsyncDelegate = Expression.Lambda>(callExpression, new[] + Func cancellableAsyncDelegate = Expression.Lambda>(callExpression, new[] { instanceParameterExpression, commandParameterExpression, CancellationTokenParameterExpression }).Compile(); - Func genericDelegate = CommandHandlerDelegateBuilder.FromDelegate(attributedObjectFactory, cancellableAsyncDelegate); - - return (obj, cancellationToken) => - { - if (obj is TCommand command) - { - return genericDelegate.Invoke(command, cancellationToken); - } - - throw new ArgumentException($"Invalid command. Expected command of type {typeof(TCommand).Name} but was given {obj.GetType().Name}.", nameof(obj)); - }; + return CommandHandlerDelegateBuilder.FromDelegate(attributedObjectFactory, cancellableAsyncDelegate); } /// @@ -390,34 +199,24 @@ private Func createCancellableAsyncDelegateType of command that is handled by the CommandHandlerAttributeMethod. This should match CommandType property. /// Factory delegate which produces an instance of a given type. /// Delegate that handles a command. - private Func createNonCancellableAsyncDelegate(Func attributedObjectFactory) + private MessageHandlerDelegate BuildNonCancellableAsyncDelegate(Func attributedObjectFactory) where TAttributed : class where TCommand : class { // Create an expression that will invoke the command handler method of a given instance. - var instanceParameterExpression = Expression.Parameter(typeof(TAttributed), "instance"); - var commandParameterExpression = Expression.Parameter(typeof(TCommand), "command"); - var callMethodExpression = Expression.Call(instanceParameterExpression, MethodInfo, commandParameterExpression); + ParameterExpression instanceParameterExpression = Expression.Parameter(typeof(TAttributed), "instance"); + ParameterExpression commandParameterExpression = Expression.Parameter(typeof(TCommand), "command"); + MethodCallExpression callMethodExpression = Expression.Call(instanceParameterExpression, MethodInfo, commandParameterExpression); // Lambda signature: // (instance, command) => instance.HandleCommandAsync(command); - var nonCancellableAsyncDelegate = Expression.Lambda>(callMethodExpression, new[] + Func nonCancellableAsyncDelegate = Expression.Lambda>(callMethodExpression, new[] { instanceParameterExpression, commandParameterExpression }).Compile(); - Func genericDelegate = CommandHandlerDelegateBuilder.FromDelegate(attributedObjectFactory, nonCancellableAsyncDelegate); - - return (obj, cancellationToken) => - { - if (obj is TCommand command) - { - return genericDelegate.Invoke(command, cancellationToken); - } - - throw new ArgumentException($"Invalid command. Expected command of type {typeof(TCommand).Name} but was given {obj.GetType().Name}.", nameof(obj)); - }; + return CommandHandlerDelegateBuilder.FromDelegate(attributedObjectFactory, nonCancellableAsyncDelegate); } /// @@ -427,34 +226,36 @@ private Func createNonCancellableAsyncDelegate< /// Type of command that is handled by the CommandHandlerAttributeMethod. This should match CommandType property. /// Factory delegate which produces an instance of a given type. /// Delegate that handles a command. - private Func createWrappedSyncDelegate(Func attributedObjectFactory) + private MessageHandlerDelegate BuildWrappedSyncDelegate(Func attributedObjectFactory) where TAttributed : class where TCommand : class { // Create an expression that will invoke the command handler method of a given instance. - var instanceParameterExpression = Expression.Parameter(typeof(TAttributed), "instance"); - var commandParameterExpression = Expression.Parameter(typeof(TCommand), "command"); - var callExpression = Expression.Call(instanceParameterExpression, MethodInfo, commandParameterExpression); + ParameterExpression instanceParameterExpression = Expression.Parameter(typeof(TAttributed), "instance"); + ParameterExpression commandParameterExpression = Expression.Parameter(typeof(TCommand), "command"); + MethodCallExpression callExpression = Expression.Call(instanceParameterExpression, MethodInfo, commandParameterExpression); // Lambda signature: // (instance, command) => instance.HandleCommand(command); - var action = Expression.Lambda>(callExpression, new[] + Action action = Expression.Lambda>(callExpression, new[] { instanceParameterExpression, commandParameterExpression }).Compile(); - Func genericDelegate = CommandHandlerDelegateBuilder.FromDelegate(attributedObjectFactory, action); - - return (obj, cancellationToken) => - { - if (obj is TCommand command) - { - return genericDelegate.Invoke(command, cancellationToken); - } + return CommandHandlerDelegateBuilder.FromDelegate(attributedObjectFactory, action); + } - throw new ArgumentException($"Invalid command. Expected command of type {typeof(TCommand).Name} but was given {obj.GetType().Name}.", nameof(obj)); - }; + /// + /// Invoke the specified method to build a delegate that can handle this CommandHandlerAttributeMethod's command type. + /// + /// Method to invoke. + /// Delegate that can handle this CommandHandlerAttributeMethod's command type. + private MessageHandlerDelegate InvokeDelegateBuilderMethod(MethodInfo openGenericBuildDelegateMethodInfo) + { + return (MessageHandlerDelegate)openGenericBuildDelegateMethodInfo + .MakeGenericMethod(DeclaringType, CommandType) + .Invoke(this, new[] { InstanceFactory }); } #endregion Functions diff --git a/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerDelegateBuilder.cs b/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerDelegateBuilder.cs index e5270ab..bad04f5 100644 --- a/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerDelegateBuilder.cs +++ b/Src/Xer.Cqrs.CommandStack.Extensions.Attributes/CommandHandlerDelegateBuilder.cs @@ -9,10 +9,10 @@ internal class CommandHandlerDelegateBuilder { #region From Delegate - internal static Func FromDelegate(Func attributedObjectFactory, - Func nonCancellableAsyncDelegate) - where TAttributed : class - where TCommand : class + internal static MessageHandlerDelegate FromDelegate(Func attributedObjectFactory, + Func nonCancellableAsyncDelegate) + where TAttributed : class + where TCommand : class { if (attributedObjectFactory == null) { @@ -32,14 +32,20 @@ internal static Func FromDelegate FromDelegate(Func attributedObjectFactory, - Func cancellableAsyncDelegate) - where TAttributed : class - where TCommand : class + internal static MessageHandlerDelegate FromDelegate(Func attributedObjectFactory, + Func cancellableAsyncDelegate) + where TAttributed : class + where TCommand : class { if (attributedObjectFactory == null) { @@ -59,14 +65,20 @@ internal static Func FromDelegate FromDelegate(Func attributedObjectFactory, - Action action) - where TAttributed : class - where TCommand : class + internal static MessageHandlerDelegate FromDelegate(Func attributedObjectFactory, + Action action) + where TAttributed : class + where TCommand : class { if (attributedObjectFactory == null) { @@ -88,8 +100,14 @@ internal static Func FromDelegate(Func /// Type of command. /// Message handler registration. - /// Command handler method object built from a method marked with [CommandHandler] attribute. + /// Message handler delegate built from a method marked with [CommandHandler] attribute. private static void registerMessageHandlerDelegate(SingleMessageHandlerRegistration registration, - CommandHandlerAttributeMethod commandHandlerMethod) + MessageHandlerDelegate messageHandlerDelegate) where TCommand : class { // Create delegate and register. - registration.Register(commandHandlerMethod.CreateCommandHandlerDelegate()); + registration.Register(messageHandlerDelegate.Invoke); } #endregion Functions diff --git a/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Entities/LoggingCommandDelegator.cs b/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Entities/LoggingCommandDelegator.cs deleted file mode 100644 index 5e2173d..0000000 --- a/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Entities/LoggingCommandDelegator.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Xer.Delegator; -using Xunit.Abstractions; - -namespace Xer.Cqrs.CommandStack.Extensions.Attributes.Tests.Entities -{ - public class LoggingCommandDelegator : IMessageDelegator - { - private readonly IMessageDelegator _inner; - private readonly ITestOutputHelper _outputHelper; - - public LoggingCommandDelegator(IMessageDelegator inner, ITestOutputHelper outputHelper) - { - _inner = inner; - _outputHelper = outputHelper; - } - public async Task SendAsync(TMessage message, CancellationToken cancellationToken = default(CancellationToken)) where TMessage : class - { - try - { - await _inner.SendAsync(message, cancellationToken); - } - catch(Exception ex) - { - _outputHelper.WriteLine(ex.ToString()); - throw; - } - } - } -} \ No newline at end of file diff --git a/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Registration/AttributeRegistrationTests.cs b/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Registration/AttributeRegistrationTests.cs index a8548be..fe9ef14 100644 --- a/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Registration/AttributeRegistrationTests.cs +++ b/Tests/Xer.Cqrs.CommandStack.Extensions.Attributes.Tests/Registration/AttributeRegistrationTests.cs @@ -15,7 +15,7 @@ namespace Xer.Cqrs.CommandStack.Extensions.Attributes.Tests.Registration { public class AttributeRegistrationTests { - #region RegisterCommandHandlerAttributes Method + #region RegisterCommandHandlersByAttribute Method public class RegisterCommandHandlerAttributes { @@ -48,7 +48,7 @@ public async Task ShouldRegisterAllMethodsOfTypeThatIsMarkedWithCommandHandlerAt } [Fact] - public async Task ShouldRegisterAllCommandHandlerAttributeMethodObjects() + public async Task ShouldRegisterAllCommandHandlerAttributeMethods() { var commandHandler = new TestAttributedCommandHandler(_outputHelper); @@ -92,6 +92,6 @@ public void ShouldNotAllowSyncMethodsWithCancellationToken() } } - #endregion RegisterCommandHandlerAttributes Method + #endregion RegisterCommandHandlersByAttribute Method } }