Documentation Coverage Report
Current view: top level - Cqrs/Configuration - BusRegistrar.cs Hit Total Coverage
Version: 2.2 Artefacts: 12 12 100.0 %
Date: 2018-08-07 15:04:50

          Line data    Source code
       1             : #region Copyright
       2             : // // -----------------------------------------------------------------------
       3             : // // <copyright company="Chinchilla Software Limited">
       4             : // //   Copyright Chinchilla Software Limited. All rights reserved.
       5             : // // </copyright>
       6             : // // -----------------------------------------------------------------------
       7             : #endregion
       8             : 
       9             : using System;
      10             : using System.Collections.Generic;
      11             : using System.Linq;
      12             : using System.Reflection;
      13             : using System.Security;
      14             : using cdmdotnet.Logging;
      15             : using Cqrs.Bus;
      16             : using Cqrs.Commands;
      17             : using Cqrs.Events;
      18             : using Cqrs.Messages;
      19             : 
      20             : namespace Cqrs.Configuration
      21             : {
      22             :         /// <summary>
      23             :         /// Triggers the <see cref="IEventHandlerRegistrar"/> and <see cref="ICommandHandlerRegistrar"/> if they are registered in the <see cref="IDependencyResolver"/>.
      24             :         /// </summary>
      25             :         public class BusRegistrar
      26           1 :         {
      27             :                 /// <summary>
      28             :                 /// Gets or set the <see cref="IDependencyResolver"/>.
      29             :                 /// </summary>
      30             :                 protected IDependencyResolver DependencyResolver { get; private set; }
      31             : 
      32             :                 /// <summary>
      33             :                 /// A <see cref="Func{Type, Type, THandlerRegistrar}"/> to use in-place of <see cref="IEventHandlerRegistrar"/>
      34             :                 /// </summary>
      35             :                 public static Func<Type, Type, IHandlerRegistrar> GetEventHandlerRegistrar { get; set; }
      36             : 
      37             :                 /// <summary>
      38             :                 /// A <see cref="Func{Type, Type, THandlerRegistrar}"/> to use in-place of <see cref="ICommandHandlerRegistrar"/>
      39             :                 /// </summary>
      40             :                 public static Func<Type, Type, IHandlerRegistrar> GetCommandHandlerRegistrar { get; set; }
      41             : 
      42             :                 /// <summary>
      43             :                 /// Instantiates a new instance of <see cref="BusRegistrar"/>.
      44             :                 /// </summary>
      45           1 :                 public BusRegistrar(IDependencyResolver dependencyResolver)
      46             :                 {
      47             :                         if(dependencyResolver == null)
      48             :                                 throw new ArgumentNullException("dependencyResolver");
      49             : 
      50             :                         DependencyResolver = dependencyResolver;
      51             :                         if (GetEventHandlerRegistrar == null)
      52             :                                 GetEventHandlerRegistrar = (messageType, handlerDelegateTargetedType) => DependencyResolver.Resolve<IEventHandlerRegistrar>();
      53             :                         if (GetCommandHandlerRegistrar == null)
      54             :                                 GetCommandHandlerRegistrar = (messageType, handlerDelegateTargetedType) => DependencyResolver.Resolve<ICommandHandlerRegistrar>();
      55             :                 }
      56             : 
      57             :                 /// <summary>
      58             :                 /// Registers all <see cref="IEventHandler"/> and <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> instances found in the <see cref="Assembly"/> for each <see cref="Type"/> in <paramref name="typesFromAssemblyContainingMessages"/>.
      59             :                 /// </summary>
      60             :                 /// <param name="typesFromAssemblyContainingMessages">A collection of <see cref="Type"/> to track back to their containing <see cref="Assembly"/> and scan.</param>
      61           1 :                 public virtual void Register(params Type[] typesFromAssemblyContainingMessages)
      62             :                 {
      63             :                         var eventHandlerRegistrar = DependencyResolver.Resolve<IEventHandlerRegistrar>();
      64             :                         if (eventHandlerRegistrar != null)
      65             :                                 Register(true, ResolveEventHandlerInterface, true, typesFromAssemblyContainingMessages);
      66             : 
      67             :                         var commandHandlerRegistrar = DependencyResolver.Resolve<ICommandHandlerRegistrar>();
      68             :                         if (commandHandlerRegistrar != null)
      69             :                                 Register(false, ResolveCommandHandlerInterface, false, typesFromAssemblyContainingMessages);
      70             :                 }
      71             : 
      72             :                 /// <summary>
      73             :                 /// Registers all <see cref="IHandler"/> instances found in the <see cref="Assembly"/> for each <see cref="Type"/> in <paramref name="typesFromAssemblyContainingMessages"/>.
      74             :                 /// </summary>
      75             :                 /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
      76             :                 /// <param name="resolveMessageHandlerInterface"><see cref="ResolveEventHandlerInterface"/> or <see cref="ResolveCommandHandlerInterface"/></param>
      77             :                 /// <param name="skipCommandHandlers">Indicates if registering of <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> is enabled.</param>
      78             :                 /// <param name="typesFromAssemblyContainingMessages">A collection of <see cref="Type"/> to track back to their containing <see cref="Assembly"/> and scan.</param>
      79           1 :                 public virtual void Register(bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, bool skipCommandHandlers, params Type[] typesFromAssemblyContainingMessages)
      80             :                 {
      81             :                         foreach (Type typesFromAssemblyContainingMessage in typesFromAssemblyContainingMessages)
      82             :                         {
      83             :                                 Assembly executorsAssembly = typesFromAssemblyContainingMessage.Assembly;
      84             :                                 HandlerTypeInformation[] executorTypes = executorsAssembly
      85             :                                         .GetTypes()
      86             :                                         .Select(type => new HandlerTypeInformation { Type = type, Interfaces = resolveMessageHandlerInterface(type) })
      87             :                                         .Where(type => type.Interfaces != null && type.Interfaces.Any())
      88             :                                         .ToArray();
      89             : 
      90             :                                 Register(trueForEventsFalseForCommands, resolveMessageHandlerInterface, skipCommandHandlers, executorTypes);
      91             :                         }
      92             :                 }
      93             : 
      94             :                 /// <summary>
      95             :                 /// Information explaining the registration to make
      96             :                 /// </summary>
      97             :                 public class HandlerTypeInformation
      98           1 :                 {
      99             :                         /// <summary>
     100             :                         /// The <see cref="Type"/> of the hanlder to register
     101             :                         /// </summary>
     102             :                         public Type Type { get; set; }
     103             : 
     104             :                         /// <summary>
     105             :                         /// The Handler type to resolve to, so either <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>
     106             :                         /// </summary>
     107             :                         public IEnumerable<Type> Interfaces { get; set; }
     108             :                 }
     109             : 
     110             :                 /// <summary>
     111             :                 /// Registers all <see cref="IHandler"/> instances in the provided <paramref name="executorTypes"/>.
     112             :                 /// </summary>
     113             :                 /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
     114             :                 /// <param name="resolveMessageHandlerInterface"><see cref="ResolveEventHandlerInterface"/> or <see cref="ResolveCommandHandlerInterface"/></param>
     115             :                 /// <param name="skipCommandHandlers">Indicates if registering of <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> is enabled.</param>
     116             :                 /// <param name="executorTypes">A collection of <see cref="Type"/> to register.</param>
     117           1 :                 public virtual void Register(bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, bool skipCommandHandlers, params HandlerTypeInformation[] executorTypes)
     118             :                 {
     119             :                         foreach (var executorType in executorTypes)
     120             :                         {
     121             :                                 foreach (Type @interface in executorType.Interfaces)
     122             :                                 {
     123             :                                         Type safeExecutorType = executorType.Type;
     124             :                                         if (safeExecutorType.IsGenericType && safeExecutorType.Name == typeof(DtoCommandHandler<,>).Name)
     125             :                                         {
     126             :                                                 if (skipCommandHandlers)
     127             :                                                         continue;
     128             :                                                 Type[] genericArguments = safeExecutorType.GetGenericArguments().Take(2).ToArray();
     129             :                                                 safeExecutorType = safeExecutorType.MakeGenericType(genericArguments.Take(2).ToArray());
     130             :                                         }
     131             :                                         InvokeHandler(@interface, trueForEventsFalseForCommands, resolveMessageHandlerInterface, safeExecutorType);
     132             :                                 }
     133             :                         }
     134             :                 }
     135             : 
     136             :                 /// <summary>
     137             :                 /// Extract the <see cref="IHandlerRegistrar.RegisterHandler{TMessage}(System.Action{TMessage},System.Type,bool)"/> method of <see cref="GetEventHandlerRegistrar"/> or <see cref="GetCommandHandlerRegistrar"/>.
     138             :                 /// Create an <see cref="Action"/> around the provided <paramref name="executorType"/>
     139             :                 /// Then register the created <see cref="Action"/> using the extracted <see cref="IHandlerRegistrar.RegisterHandler{TMessage}(System.Action{TMessage},System.Type,bool)"/> method
     140             :                 /// </summary>
     141             :                 /// <param name="interface">The <see cref="Type"/> of <see cref="IHandler"/></param>
     142             :                 /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
     143             :                 /// <param name="resolveMessageHandlerInterface"><see cref="ResolveEventHandlerInterface"/> or <see cref="ResolveCommandHandlerInterface"/></param>
     144             :                 /// <param name="executorType">The <see cref="Type"/> of the event handler that will do the handling</param>
     145           1 :                 protected virtual void InvokeHandler(Type @interface, bool trueForEventsFalseForCommands, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface, Type executorType)
     146             :                 {
     147             :                         MethodInfo registerExecutorMethod = null;
     148             : 
     149             :                         MethodInfo originalRegisterExecutorMethod = (trueForEventsFalseForCommands ? GetEventHandlerRegistrar(null, executorType) : GetCommandHandlerRegistrar(null, executorType))
     150             :                                 .GetType()
     151             :                                 .GetMethods(BindingFlags.Instance | BindingFlags.Public)
     152             :                                 .Where(mi => mi.Name == "RegisterHandler")
     153             :                                 .Where(mi => mi.IsGenericMethod)
     154             :                                 .Where(mi => mi.GetGenericArguments().Count() == 1)
     155             :                                 .Single(mi => mi.GetParameters().Count() == 3);
     156             : 
     157             :                         IList<Type> interfaceGenericArguments = @interface.GetGenericArguments().ToList();
     158             :                         if (interfaceGenericArguments.Count == 2)
     159             :                         {
     160             :                                 Type commandType = interfaceGenericArguments[1];
     161             :                                 Type[] genericArguments = commandType.GetGenericArguments();
     162             :                                 if (genericArguments.Length == 1)
     163             :                                         if (typeof (IEvent<>).MakeGenericType(genericArguments.First()) == commandType)
     164             :                                                 return;
     165             : 
     166             :                                 registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
     167             :                         }
     168             :                         else
     169             :                         {
     170             :                                 foreach (Type commandType in interfaceGenericArguments)
     171             :                                 {
     172             :                                         try
     173             :                                         {
     174             :                                                 registerExecutorMethod = BuildExecutorMethod(originalRegisterExecutorMethod, executorType, commandType);
     175             :                                                 break;
     176             :                                         }
     177             :                                         catch (VerificationException)
     178             :                                         {
     179             :                                         }
     180             :                                         catch (ArgumentException)
     181             :                                         {
     182             :                                         }
     183             :                                 }
     184             :                         }
     185             : 
     186             :                         if (registerExecutorMethod == null)
     187             :                                 throw new InvalidOperationException("No executor method could be compiled for " + @interface.FullName);
     188             : 
     189             :                         HandlerDelegate handlerDelegate = BuildDelegateAction(executorType, resolveMessageHandlerInterface);
     190             : 
     191             :                         InvokeHandlerDelegate(registerExecutorMethod, trueForEventsFalseForCommands, handlerDelegate);
     192             :                 }
     193             : 
     194             :                 /// <summary>
     195             :                 /// Invokes <paramref name="handlerDelegate"/>, fetching the corresponding "HoldMessageLock" configuration setting 
     196             :                 /// </summary>
     197             :                 /// <param name="registerExecutorMethod">The <see cref="MethodInfo"/> to use to invoke <paramref name="handlerDelegate"/>.</param>
     198             :                 /// <param name="trueForEventsFalseForCommands">Indicates if this is registers <see cref="IEventHandler"/> or <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/>.</param>
     199             :                 /// <param name="handlerDelegate">The actual <see cref="HandlerDelegate"/> that gets executed.</param>
     200           1 :                 protected virtual void InvokeHandlerDelegate(MethodInfo registerExecutorMethod, bool trueForEventsFalseForCommands, HandlerDelegate handlerDelegate)
     201             :                 {
     202             :                         Type messageType = null;
     203             :                         bool holdMessageLock;
     204             :                         try
     205             :                         {
     206             :                                 messageType = registerExecutorMethod.GetParameters()[0].ParameterType.GetGenericArguments()[0];
     207             :                                 string messageTypeName = messageType.FullName;
     208             :                                 string configuration = string.Format("{0}<{1}>.HoldMessageLock", handlerDelegate.TargetedType.FullName, messageTypeName);
     209             :                                 // If this cannot be parsed then assume the safe route that this must be required to hold the lock.
     210             :                                 if (!bool.TryParse(DependencyResolver.Resolve<IConfigurationManager>().GetSetting(configuration), out holdMessageLock))
     211             :                                         holdMessageLock = true;
     212             :                         }
     213             :                         catch
     214             :                         {
     215             :                                 holdMessageLock = true;
     216             :                         }
     217             :                         registerExecutorMethod.Invoke(trueForEventsFalseForCommands ? GetEventHandlerRegistrar(messageType, handlerDelegate.TargetedType) : GetCommandHandlerRegistrar(messageType, handlerDelegate.TargetedType), new object[] { handlerDelegate.Delegate, handlerDelegate.TargetedType, holdMessageLock });
     218             :                 }
     219             : 
     220             :                 /// <summary>
     221             :                 /// Builds a <see cref="HandlerDelegate"/> that will resolve the provided <paramref name="executorType"/> and invoke the Handle method, when executed.
     222             :                 /// </summary>
     223             :                 /// <param name="executorType">The type of <see cref="IHandler"/> to resolve.></param>
     224             :                 /// <param name="resolveMessageHandlerInterface">Not used.</param>
     225           1 :                 protected virtual HandlerDelegate BuildDelegateAction(Type executorType, Func<Type, IEnumerable<Type>> resolveMessageHandlerInterface)
     226             :                 {
     227             :                         Action<dynamic> handlerDelegate = x =>
     228             :                         {
     229             :                                 dynamic handler = DependencyResolver.Resolve(executorType);
     230             :                                 try
     231             :                                 {
     232             :                                         handler.Handle(x);
     233             :                                 }
     234             :                                 catch (NotImplementedException exception)
     235             :                                 {
     236             :                                         var logger = DependencyResolver.Resolve<ILogger>();
     237             :                                         logger.LogInfo(string.Format("An event message arrived of the type '{0}' went to a handler of type '{1}' but was not implemented.", x.GetType().FullName, handler.GetType().FullName), exception: exception);
     238             :                                 }
     239             :                         };
     240             : 
     241             :                         return new HandlerDelegate { Delegate = handlerDelegate, TargetedType = executorType };
     242             :                 }
     243             : 
     244             :                 /// <summary>
     245             :                 /// Builds a method replacing the generic type with <paramref name="commandType"/>.
     246             :                 /// </summary>
     247           1 :                 protected virtual MethodInfo BuildExecutorMethod(MethodInfo originalRegisterExecutorMethod, Type executorType, Type commandType)
     248             :                 {
     249             :                         Type safeCommandType = commandType;
     250             :                         if (safeCommandType.IsGenericType && safeCommandType.Name == typeof(DtoCommand<,>).Name && executorType.IsGenericType && executorType.Name == typeof(DtoCommandHandler<,>).Name)
     251             :                         {
     252             :                                 Type[] genericArguments = executorType.GetGenericArguments().Take(2).ToArray();
     253             :                                 safeCommandType = typeof(DtoCommand<,>).MakeGenericType(genericArguments);
     254             :                         }
     255             : 
     256             :                         return originalRegisterExecutorMethod.MakeGenericMethod(safeCommandType);
     257             :                 }
     258             : 
     259             :                 /// <summary>
     260             :                 /// Finds all <see cref="Type"/> that implement <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> that are implemented by <paramref name="type"/>.
     261             :                 /// </summary>
     262             :                 /// <param name="type">The <see cref="Type"/> to find all <see cref="IEventHandler{TAuthenticationToken,TEvent}"/> of.</param>
     263           1 :                 public virtual IEnumerable<Type> ResolveEventHandlerInterface(Type type)
     264             :                 {
     265             :                         return type
     266             :                                 .GetInterfaces()
     267             :                                 .Where
     268             :                                 (
     269             :                                         @interface =>
     270             :                                                 @interface.IsGenericType &&
     271             :                                                 (
     272             :                                                         @interface.GetGenericTypeDefinition() == typeof(IEventHandler<,>)
     273             :                                                 )
     274             :                                 );
     275             :                 }
     276             : 
     277             :                 /// <summary>
     278             :                 /// Finds all <see cref="Type"/> that implement <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> that are implemented by <paramref name="type"/>.
     279             :                 /// </summary>
     280             :                 /// <param name="type">The <see cref="Type"/> to find all <see cref="ICommandHandler{TAuthenticationToken,TCommand}"/> of.</param>
     281           1 :                 public virtual IEnumerable<Type> ResolveCommandHandlerInterface(Type type)
     282             :                 {
     283             :                         return type
     284             :                                 .GetInterfaces()
     285             :                                 .Where
     286             :                                 (
     287             :                                         @interface =>
     288             :                                                 @interface.IsGenericType &&
     289             :                                                 (
     290             :                                                         @interface.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
     291             :                                                 )
     292             :                                 );
     293             :                 }
     294             :         }
     295             : }

Generated by: LCOV version 1.12