예제 #1
0
        /// <summary>
        ///     Branches the message chain into multiple branches, where each has it's own handler or factory
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The message handler chain builder</param>
        /// <param name="firstBranch">The first branch</param>
        /// <param name="branches">The other branch(es)</param>
        public static void Branch <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Action <IChainBuilder <TMessageType> > firstBranch,
            params Action <IChainBuilder <TMessageType> >[] branches)
        {
            if (firstBranch == null)
            {
                throw new ArgumentNullException(nameof(firstBranch));
            }

            chainBuilder.Handle(
                services =>
            {
                var allBranches = new List <Action <IChainBuilder <TMessageType> > >
                {
                    firstBranch
                };

                allBranches.AddRange(branches);

                var handler = new BranchHandler <TMessageType>(allBranches);
                services.BuilderNotifier.AddNotification(handler.ChainBuilt);
                return(handler.HandleMessageAsync);
            });
        }
예제 #2
0
 /// <summary>
 /// Delay handling each message by a specified time
 /// </summary>
 /// <typeparam name="TMessageType">The message type</typeparam>
 /// <param name="chainBuilder">The builder</param>
 /// <param name="timeInMilliseconds">The time to await in milliseconds</param>
 /// <returns>The builder</returns>
 public static IChainBuilder <TMessageType> Delay <TMessageType>(
     this IChainBuilder <TMessageType> chainBuilder,
     int timeInMilliseconds)
 {
     return(chainBuilder.AddDecorator(
                nextHandler => new DelayDecorator <TMessageType>(nextHandler, TimeSpan.FromMilliseconds(timeInMilliseconds)).HandleMessageAsync));
 }
예제 #3
0
 /// <summary>
 ///     Wires up a message handler from attributes on the message handler type
 /// </summary>
 /// <typeparam name="TMessageType">The message type</typeparam>
 /// <typeparam name="THandlerType">The handler</typeparam>
 /// <param name="chainBuilder">The message handler chain builder</param>
 /// <param name="handler">The handler</param>
 /// <returns>A message handler chain builder</returns>
 public static IChainBuilder <TMessageType> WireUp <TMessageType, THandlerType>(
     this IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
     where THandlerType : IMessageHandler <TMessageType>
 {
     return(WireUpMap.Default.WireUpHandlerFromAttributes <TMessageType, THandlerType, THandlerType>(chainBuilder, handler));
 }
        /// <summary>
        ///     Use a factory method to provide a message handler instance for each message passing through
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <typeparam name="THandler">The handler type</typeparam>
        /// <param name="chainBuilder">The mhc builder</param>
        /// <param name="handlerFactory">The handler factory method</param>
        /// <param name="neverDispose">Prevent the subscription from disposing the message handler</param>
        public static void AddHandlerFactory <TMessageType, THandler>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <THandler> handlerFactory,
            bool neverDispose = false)
            where THandler : IMessageHandler <TMessageType>
        {
            if (handlerFactory == null)
            {
                throw new ArgumentNullException(nameof(handlerFactory));
            }

            if (neverDispose == false && typeof(IDisposable).IsAssignableFrom(typeof(THandler)))
            {
                chainBuilder.Handler(
                    async(message, token) =>
                {
                    var handler = handlerFactory();
                    try
                    {
                        await handler.HandleMessageAsync(message, token).ConfigureAwait(false);
                    }
                    finally
                    {
                        ((IDisposable)handler).Dispose();
                    }
                });
            }

            chainBuilder.Handler(
                (message, token) =>
            {
                var handler = handlerFactory();
                return(handler.HandleMessageAsync(message, token));
            });
        }
예제 #5
0
        /// <summary>
        ///     Wires up message handler decorators and handler from the TWireUpType's attributes
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <typeparam name="TWireUpType">The type to get wire up decorator attributes from</typeparam>
        /// <typeparam name="THandlerType">The message handler type</typeparam>
        /// <param name="builder">The message handler chain builder</param>
        /// <param name="handler">The message handler</param>
        /// <returns>A message handler chain builder</returns>
        public IChainBuilder <TMessageType> WireUpHandlerFromAttributes <TMessageType, TWireUpType, THandlerType>(
            IChainBuilder <TMessageType> builder,
            THandlerType handler)
            where THandlerType : IMessageHandler <TMessageType>
        {
            var type       = typeof(TWireUpType);
            var attributes = type.GetCustomAttributes(true);
            var addHandler = true;

            foreach (Attribute attribute in attributes)
            {
                var attributeType = attribute.GetType();

                if (this.attributeWireUpItems.TryGetValue(attributeType, out var mapItem))
                {
                    var addHandlerResult = mapItem.WireUpFromAttribute(attribute, builder, handler);

                    if (addHandlerResult == false)
                    {
                        addHandler = false;
                    }
                }
            }

            if (addHandler)
            {
                builder.Handler(handler);
            }

            return(builder);
        }
예제 #6
0
        /// <summary>
        ///     Wires up message handler decorators and handler from the a collection of wire up configuration objects
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <typeparam name="THandlerType">The message handler type</typeparam>
        /// <param name="builder">The message handler chain builder</param>
        /// <param name="handler">The message handler</param>
        /// <param name="wireUpConfigurationObjects">The configuration object</param>
        /// <returns>A message handler chain builder</returns>
        public IChainBuilder <TMessageType> WireUpHandlerFromConfiguration <TMessageType, THandlerType>(
            IChainBuilder <TMessageType> builder,
            THandlerType handler,
            IEnumerable <object> wireUpConfigurationObjects)
            where THandlerType : IMessageHandler <TMessageType>
        {
            var addHandler = true;

            foreach (var configurationItem in wireUpConfigurationObjects.Where(c => c != null))
            {
                if (this.configurationWireUpItems.TryGetValue(configurationItem.GetType(), out var mapItem))
                {
                    var addHandlerResult = mapItem.WireUpFromConfiguration(configurationItem, builder, handler);

                    if (addHandlerResult == false)
                    {
                        addHandler = false;
                    }
                }
            }

            if (addHandler && handler != null)
            {
                builder.Handler(handler);
            }

            return(builder);
        }
예제 #7
0
        /// <summary>
        ///     Append a second message for each message passed through
        /// </summary>
        /// <typeparam name="TMessageType">The chain message type</typeparam>
        /// <param name="chainBuilder">The mch builder</param>
        /// <param name="messageSelector">The function used to create the new message</param>
        /// <returns>The same mch builder</returns>
        public static IChainBuilder <TMessageType> AppendMany <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, IEnumerable <TMessageType> > messageSelector)
        {
            if (messageSelector == null)
            {
                return(chainBuilder);
            }

            return(chainBuilder.AddDecorator(
                       innerMessageHandler =>
            {
                return (message, token) =>
                {
#pragma warning disable CC0031 // Check for null before calling a delegate
                    var originalMessageTask = innerMessageHandler(message, token);
                    var newMessages = messageSelector(message);
                    if (newMessages == null)
                    {
                        return originalMessageTask;
                    }

                    var newMessagesTask = Task.WhenAll(newMessages.Select(msg => innerMessageHandler(msg, token)));
#pragma warning restore CC0031 // Check for null before calling a delegate
                    return Task.WhenAll(originalMessageTask, newMessagesTask);
                };
            }));
        }
예제 #8
0
 /// <summary>
 /// Wires up a message handler from configuration on the message handler type
 /// </summary>
 /// <typeparam name="TMessageType">
 /// The message type
 /// </typeparam>
 /// <typeparam name="THandlerType">
 /// The handler
 /// </typeparam>
 /// <param name="chainBuilder">
 /// The message handler chain builder
 /// </param>
 /// <param name="handler">
 /// The handler
 /// </param>
 /// <param name="wireUpConfigurationObjects">
 /// The wire Up Configuration Objects.
 /// </param>
 /// <returns>
 /// A message handler chain builder
 /// </returns>
 public static IChainBuilder <TMessageType> WireUp <TMessageType, THandlerType>(
     this IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler,
     IEnumerable <object> wireUpConfigurationObjects)
     where THandlerType : IMessageHandler <TMessageType>
 {
     return(WireUpMap.Default.WireUpHandlerFromConfiguration(chainBuilder, handler, wireUpConfigurationObjects));
 }
예제 #9
0
 /// <summary>
 /// Limits throughput to a fixed number of messages during a period of time
 /// </summary>
 /// <typeparam name="TMessageType">The message type</typeparam>
 /// <param name="chainBuilder">The message handler chain builder</param>
 /// <param name="maxMessagesPerPeriod">The maximum number of messages per period</param>
 /// <param name="periodSpan">The period span</param>
 /// <returns>The message handler chain builder</returns>
 public static IChainBuilder <TMessageType> LimitedThroughputFireAndForget <TMessageType>(
     this IChainBuilder <TMessageType> chainBuilder,
     int maxMessagesPerPeriod,
     TimeSpan?periodSpan = null)
 {
     return(chainBuilder.AddDecorator(
                nextHandler => new LimitedThroughputFireAndForgetDecorator <TMessageType>(nextHandler, maxMessagesPerPeriod, periodSpan ?? TimeSpan.FromSeconds(1))));
 }
 protected override bool WireUpFromConfiguration <TMessageType, THandlerType>(
     LimitedThroughputConfiguration configuration,
     IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
 {
     chainBuilder.LimitedThroughput(configuration.MaxNumberOfMessagesPerPeriod, configuration.Period);
     return(true);
 }
 protected override bool WireUpFromConfiguration <TMessageType, THandlerType>(
     SoftFireAndForgetConfiguration configuration,
     IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
 {
     chainBuilder.SoftFireAndForget();
     return(true);
 }
 protected override bool WireUpFromAttribute <TMessageType, THandlerType>(
     LimitedThroughputAttribute attribute,
     IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
 {
     chainBuilder.LimitedThroughput(attribute.MaxNumberOfMessagesPerPeriod, attribute.Period);
     return(true);
 }
예제 #13
0
 protected override bool WireUpFromConfiguration <TMessageType, THandlerType>(
     NoDuplicatesConfiguration configuration,
     IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
 {
     WireUp <TMessageType>(chainBuilder, configuration.PropertyName);
     return(true);
 }
예제 #14
0
 protected override bool WireUpFromAttribute <TMessageType, THandlerType>(
     DelayAttribute attribute,
     IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
 {
     chainBuilder.Delay(attribute.Delay);
     return(true);
 }
예제 #15
0
 /// <summary>
 /// Make the rest of the message handler chain a Weak Reference.
 /// This means that this message handler chain will not prevent the handler from being garbage collected. If the handler is garbage collected, the subscription will be disposed the next time a message passes through.
 /// </summary>
 /// <typeparam name="TMessageType">The message type</typeparam>
 /// <param name="chainBuilder">The builder</param>
 /// <param name="handler">The object to monitor for garbage collection</param>
 /// <param name="weakReferenceGarbageCollector">A weak reference garbage collector</param>
 /// <returns>The builder</returns>
 public static IChainBuilder <TMessageType> WeakReference <TMessageType>(
     this IChainBuilder <TMessageType> chainBuilder,
     IMessageHandler <TMessageType> handler,
     IWeakReferenceGarbageCollector weakReferenceGarbageCollector)
 {
     chainBuilder.Handle(services => new WeakReferenceDecorator <TMessageType>(handler, services.BuilderNotifier, weakReferenceGarbageCollector).HandleMessageAsync);
     return(chainBuilder);
 }
예제 #16
0
 protected override bool WireUpFromConfiguration <TMessageType, THandlerType>(
     DelayConfiguration configuration,
     IChainBuilder <TMessageType> chainBuilder,
     THandlerType handler)
 {
     chainBuilder.Delay(configuration.Delay);
     return(true);
 }
예제 #17
0
        /// <summary>
        ///     Retry the message dispatch
        /// </summary>
        /// <typeparam name="TMessageType">Message type</typeparam>
        /// <param name="chainBuilder">The message handler chain builder.</param>
        /// <param name="maxNumberOfAttempts">The maximum number of attempts to try.</param>
        /// <param name="retryDelay">The delay between retries.</param>
        /// <returns>The MHC builder</returns>
        public static IChainBuilder <TMessageType> Retry <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            int maxNumberOfAttempts,
            TimeSpan retryDelay)
        {
            var builder = new RetryDecoratorBuilder <TMessageType>().MaximumNumberOfAttempts(maxNumberOfAttempts).RetryDelays(retryDelay);

            return(chainBuilder.AddDecorator(nextHandler => new RetryDecorator <TMessageType>(nextHandler, builder).HandleMessageAsync));
        }
예제 #18
0
        protected override bool WireUpFromAttribute <TMessageType, THandlerType>(
            NoDuplicatesAttribute attribute,
            IChainBuilder <TMessageType> chainBuilder,
            THandlerType handler)
        {
            var propertyName = attribute.PropertyName;

            WireUp <TMessageType>(chainBuilder, propertyName);
            return(true);
        }
예제 #19
0
        /// <summary>
        ///     Append a range of messages for each message passed through
        /// </summary>
        /// <typeparam name="TMessageType">The chain message type</typeparam>
        /// <param name="chainBuilder">The mch builder</param>
        /// <param name="messageSelector">The function used to create the new message</param>
        /// <returns>The same mch builder</returns>
        public static IChainBuilder <TMessageType> AppendMany <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, CancellationToken, Task <IEnumerable <TMessageType> > > messageSelector)
        {
            if (messageSelector == null)
            {
                return(chainBuilder);
            }

            return(chainBuilder.AppendMany(c => c.Select(messageSelector)));
        }
예제 #20
0
        /// <summary>
        ///     Add a decorator to the message handler chain builder
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The mhc builder</param>
        /// <param name="addFunc">The method that returns the </param>
        /// <returns>The mhc builder</returns>
        public static IChainBuilder <TMessageType> AddDecorator <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <Func <TMessageType, CancellationToken, Task>, ChainBuilderSetupServices, ChainDecorator <TMessageType> > addFunc)
        {
            if (addFunc == null)
            {
                throw new ArgumentNullException(nameof(addFunc));
            }

            return(chainBuilder.Decorate((previousHandler, services) => addFunc(previousHandler, services).HandleMessageAsync));
        }
예제 #21
0
        /// <summary>
        ///     Add a decorator to the message handler chain builder
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The mhc builder</param>
        /// <param name="decoratorBuilder">The decorator builder</param>
        /// <returns>The mhc builder</returns>
        public static IChainBuilder <TMessageType> AddDecorator <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            IDecoratorBuilder <TMessageType> decoratorBuilder)
        {
            if (decoratorBuilder == null)
            {
                throw new ArgumentNullException(nameof(decoratorBuilder));
            }

            return(chainBuilder.AddDecorator(decoratorBuilder.BuildDecorator()));
        }
예제 #22
0
        /// <summary>
        ///     Set the chain message handler
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The mhc builder</param>
        /// <param name="messageHandler">The ISimpleMessageHandler to invoke</param>
        /// <returns>The mhc builder</returns>
        public static IChainBuilder <TMessageType> Handler <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            IMessageHandler <TMessageType> messageHandler)
        {
            if (messageHandler == null)
            {
                throw new ArgumentNullException(nameof(messageHandler));
            }

            return(chainBuilder.Handler(messageHandler.HandleMessageAsync));
        }
예제 #23
0
        /// <summary>
        ///     Set the chain handler method or lambda method. Use this overload if you need async but no cancellation token
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The mhc builder</param>
        /// <param name="handlerFunc">The method to invoke</param>
        /// <returns>The mhc builder</returns>
        public static IChainBuilder <TMessageType> Handler <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, Task> handlerFunc)
        {
            if (handlerFunc == null)
            {
                throw new ArgumentNullException(nameof(handlerFunc));
            }

            return(chainBuilder.Handler((message, token) => handlerFunc(message)));
        }
예제 #24
0
        /// <summary>
        ///     Set the chain handler method or lambda method. Use this overload if your handler has no need for async or
        ///     cancellationToken
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The mhc builder</param>
        /// <param name="handlerFunc">The method to invoke</param>
        /// <returns>The mhc builder</returns>
        public static IChainBuilder <TMessageType> Handler <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, CancellationToken, Task> handlerFunc)
        {
            if (handlerFunc == null)
            {
                throw new ArgumentNullException(nameof(handlerFunc));
            }

            return(chainBuilder.Handle(services => handlerFunc));
        }
예제 #25
0
        public static IChainBuilder <TMessageType> Distinct <TMessageType, TKeyType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, TKeyType> keySelector)
        {
            if (keySelector == null)
            {
                throw new ArgumentNullException(nameof(keySelector));
            }

            return(chainBuilder.AddDecorator(nextHandler => new DistinctDecorator <TMessageType, TKeyType>(nextHandler, keySelector)));
        }
예제 #26
0
        /// <summary>
        /// Filters messages based on a predicate
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The builder</param>
        /// <param name="predicate">An async function to test each message for a condition</param>
        /// <returns>The builder</returns>
        public static IChainBuilder <TMessageType> Where <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, bool> predicate)
        {
            if (predicate == null)
            {
                return(chainBuilder);
            }

            return(chainBuilder.AddDecorator(CreateWhereDecoratorFunc(predicate)));
        }
예제 #27
0
        /// <summary>
        ///     Handles exceptions, optionally prevents them to propagate up the chain
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The builder</param>
        /// <param name="exceptionHandlerFunc">
        ///     The method that handles the exception. If this method returns true, the exception
        ///     propagates further up the chain
        /// </param>
        /// <returns>The builder</returns>
        public static IChainBuilder <TMessageType> Exception <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, Exception, CancellationToken, Task <bool> > exceptionHandlerFunc)
        {
            if (exceptionHandlerFunc == null)
            {
                throw new ArgumentNullException(nameof(exceptionHandlerFunc));
            }

            return(chainBuilder.AddDecorator(nextHandler => new ExceptionDecorator <TMessageType>(nextHandler, exceptionHandlerFunc)));
        }
예제 #28
0
        /// <summary>
        /// Handle only the first message where the predicate returns true, then unsubscribe
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The message handler chain builder</param>
        /// <param name="asyncPredicate">The predicate</param>
        /// <returns>The message handler chain builder</returns>
        public static IChainBuilder <TMessageType> First <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, Task <bool> > asyncPredicate)
        {
            if (asyncPredicate == null)
            {
                throw new ArgumentNullException(nameof(asyncPredicate));
            }

            return(chainBuilder.Decorate((nextHandler, subscriptionServices) => new FirstAsyncDecorator <TMessageType>(nextHandler, (message, token) => asyncPredicate(message), subscriptionServices).HandleMessageAsync));
        }
예제 #29
0
 /// <summary>
 /// Handle messages as long as the predicate returns true, then unsubscribes
 /// </summary>
 /// <typeparam name="TMessageType">The message type</typeparam>
 /// <param name="chainBuilder">The message handler chain builder</param>
 /// <param name="predicate">The predicate used to determine if messages can continue to flow</param>
 /// <returns>A message handler chain builder</returns>
 public static IChainBuilder <TMessageType> TakeWhile <TMessageType>(
     this IChainBuilder <TMessageType> chainBuilder,
     Func <TMessageType, Task <bool> > predicate)
 {
     return(chainBuilder.AddDecorator((nextHandler, services) => new TakeWhileAsyncDecorator <TMessageType>(new TakeWhileAsyncDecoratorConfiguration <TMessageType>
     {
         HandlerFunc = nextHandler,
         Predicate = predicate,
         Services = services
     })));
 }
예제 #30
0
        /// <summary>
        ///     Handles exceptions, optionally prevents them to propagate up the chain
        /// </summary>
        /// <typeparam name="TMessageType">The message type</typeparam>
        /// <param name="chainBuilder">The builder</param>
        /// <param name="exceptionHandlerFunc">
        ///     The method that handles the exception. If this method returns true, the exception
        ///     propagates further up the chain
        /// </param>
        /// <returns>The builder</returns>
        public static IChainBuilder <TMessageType> Exception <TMessageType>(
            this IChainBuilder <TMessageType> chainBuilder,
            Func <TMessageType, Exception, bool> exceptionHandlerFunc)
        {
            if (exceptionHandlerFunc == null)
            {
                throw new ArgumentNullException(nameof(exceptionHandlerFunc));
            }

            return(chainBuilder.AddDecorator(
                       nextHandler => new ExceptionDecorator <TMessageType>(nextHandler, (message, exception, token) => Task.FromResult(exceptionHandlerFunc(message, exception)))));
        }