/// <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); }); }
/// <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)); }
/// <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)); }); }
/// <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); }
/// <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); }
/// <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); }; })); }
/// <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)); }
/// <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); }
protected override bool WireUpFromConfiguration <TMessageType, THandlerType>( NoDuplicatesConfiguration configuration, IChainBuilder <TMessageType> chainBuilder, THandlerType handler) { WireUp <TMessageType>(chainBuilder, configuration.PropertyName); return(true); }
protected override bool WireUpFromAttribute <TMessageType, THandlerType>( DelayAttribute attribute, IChainBuilder <TMessageType> chainBuilder, THandlerType handler) { chainBuilder.Delay(attribute.Delay); return(true); }
/// <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); }
protected override bool WireUpFromConfiguration <TMessageType, THandlerType>( DelayConfiguration configuration, IChainBuilder <TMessageType> chainBuilder, THandlerType handler) { chainBuilder.Delay(configuration.Delay); return(true); }
/// <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)); }
protected override bool WireUpFromAttribute <TMessageType, THandlerType>( NoDuplicatesAttribute attribute, IChainBuilder <TMessageType> chainBuilder, THandlerType handler) { var propertyName = attribute.PropertyName; WireUp <TMessageType>(chainBuilder, propertyName); return(true); }
/// <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))); }
/// <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)); }
/// <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())); }
/// <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)); }
/// <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))); }
/// <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)); }
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))); }
/// <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))); }
/// <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))); }
/// <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)); }
/// <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 }))); }
/// <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))))); }