Documentation Coverage Report
Current view: top level - Cqrs/Bus - RouteManager.cs Hit Total Coverage
Version: 2.2 Artefacts: 12 12 100.0 %
Date: 2017-09-22

          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 Cqrs.Commands;
      13             : using Cqrs.Events;
      14             : using Cqrs.Exceptions;
      15             : using Cqrs.Infrastructure;
      16             : using Cqrs.Messages;
      17             : 
      18             : namespace Cqrs.Bus
      19             : {
      20             :         /// <summary>
      21             :         /// Manages <see cref="Route">routes</see>.
      22             :         /// </summary>
      23             :         public class RouteManager
      24             :                 : IHandlerRegistrar
      25           1 :         {
      26             :                 /// <summary>
      27             :                 /// The <see cref="Route"/> to execute per <see cref="Type"/>
      28             :                 /// </summary>
      29             :                 protected IDictionary<Type, Route> Routes { get; private set; }
      30             : 
      31             :                 /// <summary>
      32             :                 /// A <see cref="Route"/> to execute for all <see cref="IEvent{TAuthenticationToken}"/>
      33             :                 /// </summary>
      34             :                 public Route GlobalEventRoute { get; private set; }
      35             : 
      36             :                 private static Type CommandType { get; set; }
      37             : 
      38             :                 private static Type EventType { get; set; }
      39             : 
      40             :                 /// <summary>
      41             :                 /// Instantiates a new instance of <see cref="RouteManager"/>.
      42             :                 /// </summary>
      43           1 :                 public RouteManager()
      44             :                 {
      45             :                         Routes = new Dictionary<Type, Route>();
      46             :                         GlobalEventRoute = new Route { Handlers = new List<RouteHandlerDelegate>() };
      47             :                 }
      48             : 
      49             :                 static RouteManager()
      50             :                 {
      51             :                         CommandType = typeof (ICommand<>);
      52             :                         EventType = typeof (IEvent<>);
      53             :                 }
      54             : 
      55             :                 #region Implementation of IHandlerRegistrar
      56             : 
      57             :                 /// <summary>
      58             :                 /// Register an event or command handler that will listen and respond to events or commands.
      59             :                 /// </summary>
      60           1 :                 public virtual void RegisterHandler<TMessage>(Action<TMessage> handler, Type targetedType, bool holdMessageLock = true)
      61             :                         where TMessage : IMessage
      62             :                 {
      63             :                         Route route;
      64             :                         if (!Routes.TryGetValue(typeof(TMessage), out route))
      65             :                         {
      66             :                                 route = new Route
      67             :                                 {
      68             :                                         Handlers = new List<RouteHandlerDelegate>()
      69             :                                 };
      70             :                                 Routes.Add(typeof(TMessage), route);
      71             :                         }
      72             :                         route.Handlers.Add
      73             :                         (
      74             :                                 new RouteHandlerDelegate
      75             :                                 {
      76             :                                         Delegate = DelegateAdjuster.CastArgument<IMessage, TMessage>(x => handler(x)),
      77             :                                         TargetedType = targetedType
      78             :                                 }
      79             :                         );
      80             :                 }
      81             : 
      82             :                 /// <summary>
      83             :                 /// Register an event or command handler that will listen and respond to events or commands.
      84             :                 /// </summary>
      85           1 :                 public void RegisterHandler<TMessage>(Action<TMessage> handler, bool holdMessageLock = true)
      86             :                         where TMessage : IMessage
      87             :                 {
      88             :                         RegisterHandler(handler, null, holdMessageLock);
      89             :                 }
      90             : 
      91             :                 /// <summary>
      92             :                 /// Register an event handler that will listen and respond to all events.
      93             :                 /// </summary>
      94           1 :                 public void RegisterGlobalEventHandler<TMessage>(Action<TMessage> handler, bool holdMessageLock = true) where TMessage : IMessage
      95             :                 {
      96             :                         GlobalEventRoute.Handlers.Add
      97             :                         (
      98             :                                 new RouteHandlerDelegate
      99             :                                 {
     100             :                                         Delegate = DelegateAdjuster.CastArgument<IMessage, TMessage>(x => handler(x)),
     101             :                                         TargetedType = null
     102             :                                 }
     103             :                         );
     104             :                 }
     105             : 
     106             :                 #endregion
     107             : 
     108             :                 /// <summary>
     109             :                 /// Gets the single <see cref="RouteHandlerDelegate"/> expected for handling <typeparamref name="TMessage"/>.
     110             :                 /// </summary>
     111             :                 /// <typeparam name="TMessage">The <see cref="Type"/> of <see cref="IMessage"/> to find a <see cref="RouteHandlerDelegate"/> for.</typeparam>
     112             :                 /// <param name="throwExceptionOnNoRouteHandlers">If true will throw an <see cref="Exception"/> if no <see cref="RouteHandlerDelegate"/> found.</param>
     113             :                 /// <exception cref="MultipleCommandHandlersRegisteredException">If more than one <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is an <see cref="ICommand{TAuthenticationToken}"/>.</exception>
     114             :                 /// <exception cref="NoCommandHandlerRegisteredException">If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is an <see cref="ICommand{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     115             :                 /// <exception cref="InvalidOperationException">
     116             :                 /// If more than one <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is not an <see cref="ICommand{TAuthenticationToken}"/> OR
     117             :                 /// If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is not an <see cref="ICommand{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     118           1 :                 public RouteHandlerDelegate GetSingleHandler<TMessage>(bool throwExceptionOnNoRouteHandlers = true)
     119             :                         where TMessage : IMessage
     120             :                 {
     121             :                         Route route;
     122             :                         Type messageType = typeof(TMessage);
     123             :                         bool isACommand = IsACommand(messageType);
     124             : 
     125             :                         if (Routes.TryGetValue(typeof(TMessage), out route))
     126             :                         {
     127             :                                 if (route.Handlers == null || route.Handlers.Count != 1)
     128             :                                 {
     129             :                                         if (isACommand)
     130             :                                                 throw new MultipleCommandHandlersRegisteredException(messageType);
     131             :                                         throw new InvalidOperationException("Cannot send to more than one handler.");
     132             :                                 }
     133             :                                 return route.Handlers.Single();
     134             :                         }
     135             : 
     136             :                         if (throwExceptionOnNoRouteHandlers)
     137             :                         {
     138             :                                 if (isACommand)
     139             :                                         throw new NoCommandHandlerRegisteredException(messageType);
     140             :                                 throw new InvalidOperationException("No handler registered.");
     141             :                         }
     142             : 
     143             :                         return null;
     144             :                 }
     145             : 
     146             :                 /// <summary>
     147             :                 /// Gets the single <see cref="RouteHandlerDelegate"/> expected for handling <typeparamref name="TMessage"/>.
     148             :                 /// </summary>
     149             :                 /// <typeparam name="TMessage">The <see cref="Type"/> of <see cref="IMessage"/> to find a <see cref="RouteHandlerDelegate"/> for.</typeparam>
     150             :                 /// <param name="message">The <typeparamref name="TMessage"/> to find a <see cref="RouteHandlerDelegate"/> for. </param>
     151             :                 /// <param name="throwExceptionOnNoRouteHandlers">If true will throw an <see cref="Exception"/> if no <see cref="RouteHandlerDelegate"/> found.</param>
     152             :                 /// <exception cref="MultipleCommandHandlersRegisteredException">If more than one <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is an <see cref="ICommand{TAuthenticationToken}"/>.</exception>
     153             :                 /// <exception cref="NoCommandHandlerRegisteredException">If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is an <see cref="ICommand{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     154             :                 /// <exception cref="InvalidOperationException">
     155             :                 /// If more than one <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is not an <see cref="ICommand{TAuthenticationToken}"/> OR
     156             :                 /// If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is not an <see cref="ICommand{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     157           1 :                 public RouteHandlerDelegate GetSingleHandler<TMessage>(TMessage message, bool throwExceptionOnNoRouteHandlers = true)
     158             :                         where TMessage : IMessage
     159             :                 {
     160             :                         Route route;
     161             :                         Type messageType = message.GetType();
     162             :                         bool isACommand = IsACommand(messageType);
     163             : 
     164             :                         if (Routes.TryGetValue(messageType, out route))
     165             :                         {
     166             :                                 if (route.Handlers != null)
     167             :                                 {
     168             :                                         if (route.Handlers.Count > 1)
     169             :                                         {
     170             :                                                 if (isACommand)
     171             :                                                         throw new MultipleCommandHandlersRegisteredException(messageType);
     172             :                                                 throw new InvalidOperationException("Cannot send to more than one handler.");
     173             :                                         }
     174             :                                         if (route.Handlers.Count == 1)
     175             :                                                 return route.Handlers.Single();
     176             :                                 }
     177             :                         }
     178             : 
     179             :                         if (throwExceptionOnNoRouteHandlers)
     180             :                         {
     181             :                                 if (isACommand)
     182             :                                         throw new NoCommandHandlerRegisteredException(messageType);
     183             :                                 throw new InvalidOperationException("No handler registered.");
     184             :                         }
     185             : 
     186             :                         return null;
     187             :                 }
     188             : 
     189             :                 /// <summary>
     190             :                 /// Gets the collection <see cref="RouteHandlerDelegate"/> that are expected for handling <typeparamref name="TMessage"/>.
     191             :                 /// </summary>
     192             :                 /// <typeparam name="TMessage">The <see cref="Type"/> of <see cref="IMessage"/> to find a <see cref="RouteHandlerDelegate"/> for.</typeparam>
     193             :                 /// <param name="message">The <typeparamref name="TMessage"/> to find a <see cref="RouteHandlerDelegate"/> for. </param>
     194             :                 /// <param name="throwExceptionOnNoRouteHandlers">If true will throw an <see cref="Exception"/> if no <see cref="RouteHandlerDelegate"/> found.</param>
     195             :                 /// <exception cref="NoCommandHandlerRegisteredException">If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is an <see cref="ICommand{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     196             :                 /// <exception cref="NoEventHandlerRegisteredException"> If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is an <see cref="IEvent{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     197             :                 /// <exception cref="NoHandlerRegisteredException"> If no <see cref="RouteHandlerDelegate"/> is found and <typeparamref name="TMessage"/> is not either an <see cref="ICommand{TAuthenticationToken}"/> or an <see cref="IEvent{TAuthenticationToken}"/> and <paramref name="throwExceptionOnNoRouteHandlers"/> is true.</exception>
     198           1 :                 public IEnumerable<RouteHandlerDelegate> GetHandlers<TMessage>(TMessage message, bool throwExceptionOnNoRouteHandlers = true)
     199             :                         where TMessage : IMessage
     200             :                 {
     201             :                         Type messageType = message.GetType();
     202             :                         bool isACommand = IsACommand(messageType);
     203             :                         bool isAnEvent = IsAnEvent(messageType);
     204             : 
     205             :                         var routeHandlers = new List<RouteHandlerDelegate>();
     206             :                         if (isAnEvent && GlobalEventRoute.Handlers != null)
     207             :                                 routeHandlers.AddRange(GlobalEventRoute.Handlers);
     208             : 
     209             :                         Route route;
     210             :                         if (Routes.TryGetValue(messageType, out route))
     211             :                                 routeHandlers.AddRange(route.Handlers);
     212             : 
     213             :                         if (routeHandlers.Any())
     214             :                                 return routeHandlers;
     215             : 
     216             :                         if (throwExceptionOnNoRouteHandlers)
     217             :                         {
     218             :                                 if (isACommand)
     219             :                                         throw new NoCommandHandlerRegisteredException(messageType);
     220             :                                 if (isAnEvent)
     221             :                                         throw new NoEventHandlerRegisteredException(messageType);
     222             :                                 throw new NoHandlerRegisteredException(messageType);
     223             :                         }
     224             : 
     225             :                         return routeHandlers;
     226             :                 }
     227             : 
     228             :                 /// <summary>
     229             :                 /// Checks if the provided <paramref name="message"/> is an <see cref="ICommand{TAuthenticationToken}"/>.
     230             :                 /// </summary>
     231             :                 /// <typeparam name="TMessage">The <see cref="Type"/> of <see cref="IMessage"/> to check.</typeparam>
     232             :                 /// <param name="message">The <typeparamref name="TMessage"/> to check. </param>
     233             :                 /// <returns>true if <paramref name="message"/> is an <see cref="ICommand{TAuthenticationToken}"/>.</returns>
     234           1 :                 protected virtual bool IsACommand<TMessage>(TMessage message)
     235             :                 {
     236             :                         Type messageType = message.GetType();
     237             :                         return IsACommand(messageType);
     238             :                 }
     239             : 
     240             :                 /// <summary>
     241             :                 /// Checks if the provided <paramref name="messageType"/> implements <see cref="ICommand{TAuthenticationToken}"/>.
     242             :                 /// </summary>
     243             :                 /// <param name="messageType">The <see cref="Type"/> of object to check.</param>
     244             :                 /// <returns>true if <paramref name="messageType"/> implements <see cref="ICommand{TAuthenticationToken}"/>.</returns>
     245           1 :                 protected virtual bool IsACommand(Type messageType)
     246             :                 {
     247             :                         bool isACommand = false;
     248             :                         Type messageCommandInterface = messageType.GetInterfaces().FirstOrDefault(type => type.FullName.StartsWith(CommandType.FullName));
     249             :                         if (messageCommandInterface != null)
     250             :                         {
     251             :                                 Type[] genericArguments = messageCommandInterface.GetGenericArguments();
     252             :                                 if (genericArguments.Length == 1)
     253             :                                         isACommand = CommandType.MakeGenericType(genericArguments.Single()).IsAssignableFrom(messageType);
     254             :                         }
     255             : 
     256             :                         return isACommand;
     257             :                 }
     258             : 
     259             :                 /// <summary>
     260             :                 /// Checks if the provided <paramref name="message"/> is an <see cref="IEvent{TAuthenticationToken}"/>.
     261             :                 /// </summary>
     262             :                 /// <typeparam name="TMessage">The <see cref="Type"/> of <see cref="IMessage"/> to check.</typeparam>
     263             :                 /// <param name="message">The <typeparamref name="TMessage"/> to check. </param>
     264             :                 /// <returns>true if <paramref name="message"/> is an <see cref="IEvent{TAuthenticationToken}"/>.</returns>
     265           1 :                 protected virtual bool IsAnEvent<TMessage>(TMessage message)
     266             :                 {
     267             :                         Type messageType = message.GetType();
     268             :                         return IsAnEvent(messageType);
     269             :                 }
     270             : 
     271             :                 /// <summary>
     272             :                 /// Checks if the provided <paramref name="messageType"/> implements <see cref="IEvent{TAuthenticationToken}"/>.
     273             :                 /// </summary>
     274             :                 /// <param name="messageType">The <see cref="Type"/> of object to check.</param>
     275             :                 /// <returns>true if <paramref name="messageType"/> implements <see cref="IEvent{TAuthenticationToken}"/>.</returns>
     276           1 :                 protected virtual bool IsAnEvent(Type messageType)
     277             :                 {
     278             :                         bool isAnEvent = false;
     279             :                         Type messageEventInterface = messageType.GetInterfaces().FirstOrDefault(type => type.FullName.StartsWith(EventType.FullName));
     280             :                         if (messageEventInterface != null)
     281             :                         {
     282             :                                 Type[] genericArguments = messageEventInterface.GetGenericArguments();
     283             :                                 if (genericArguments.Length == 1)
     284             :                                         isAnEvent = EventType.MakeGenericType(genericArguments.Single()).IsAssignableFrom(messageType);
     285             :                         }
     286             : 
     287             :                         return isAnEvent;
     288             :                 }
     289             :         }
     290             : }

Generated by: LCOV version 1.10