/// <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> /// 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> /// 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)))); }
/// <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)); }
/// <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> /// 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>, ChainDecorator <TMessageType> > addFunc) { if (addFunc == null) { throw new ArgumentNullException(nameof(addFunc)); } return(chainBuilder.AddDecorator(previousHandler => addFunc(previousHandler).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> /// 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))); }
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> /// 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))))); }
/// <summary>Throws a <see cref="T:System.OperationCanceledException"></see> if the token has had cancellation requested.</summary> /// <exception cref="T:System.OperationCanceledException">The token has had cancellation requested.</exception> /// <exception cref="T:System.ObjectDisposedException">The associated <see cref="T:System.Threading.CancellationTokenSource"></see> has been disposed.</exception> /// <typeparam name="TMessageType">The chain message type</typeparam> /// <param name="chainBuilder">The mch builder</param> /// <returns>The same mch builder</returns> public static IChainBuilder <TMessageType> ThrowIfCancellationRequested <TMessageType>( this IChainBuilder <TMessageType> chainBuilder) { return(chainBuilder.AddDecorator( innerMessageHandler => { return (message, token) => { token.ThrowIfCancellationRequested(); return innerMessageHandler(message, token); }; })); }
/// <summary> /// Limits the message handler chain to X concurrent messages being handled. /// This method does not add concurrency but limits it. /// </summary> /// <typeparam name="TMessageType"> /// The type of the messages type in the message handler chain /// </typeparam> /// <param name="chainBuilder"> /// The message handler chain builder /// </param> /// <param name="config"> /// The action called to configure the semaphore /// </param> /// <returns> /// The <see cref="IChainBuilder<TMessageType>" />. /// </returns> public static IChainBuilder <TMessageType> Semaphore <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Action <SemaphoreDecoratorBuilder <TMessageType> > config) { if (config == null) { throw new ArgumentNullException(nameof(config)); } var builder = new SemaphoreDecoratorBuilder <TMessageType>(); config(builder); return(chainBuilder.AddDecorator(nextHandler => builder.Build(nextHandler))); }
/// <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="configure"> /// Action called to configure the append many options /// </param> /// <returns> /// A mch builder /// </returns> public static IChainBuilder <TMessageType> AppendMany <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Action <IAppendManyDecoratorBuilder <TMessageType> > configure) { if (configure == null) { throw new ArgumentNullException(nameof(configure)); } var builder = new AppendManyDecoratorBuilder <TMessageType>(); configure(builder); return(chainBuilder.AddDecorator(builder.BuildDecorator())); }
/// <summary> /// Prevents more than a single message with the same key from being handled concurrently. /// The keySelector defines the key. All messages identified as duplicates are dropped. /// </summary> /// <typeparam name="TMessageType"> /// The message type /// </typeparam> /// <param name="chainBuilder"> /// The message handler chain builder /// </param> /// <param name="config">An action that configures the no duplicates decorator</param> /// <returns> /// The message handler chain builder /// </returns> public static IChainBuilder <TMessageType> NoDuplicates <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Action <NoDuplicatesDecoratorBuilder <TMessageType> > config) { if (config == null) { throw new ArgumentNullException(nameof(config)); } var builder = new NoDuplicatesDecoratorBuilder <TMessageType>(); config(builder); return(chainBuilder.AddDecorator(builder)); }
/// <summary> /// Retry the message dispatch /// </summary> /// <typeparam name="TMessageType">Message type</typeparam> /// <param name="chainBuilder">The message handler chain builder.</param> /// <param name="configureRetry">Metod called to configure the retry</param> /// <returns>The MHC builder</returns> public static IChainBuilder <TMessageType> Retry <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Action <IRetryDecoratorBuilder <TMessageType> > configureRetry) { if (configureRetry == null) { throw new ArgumentNullException(nameof(configureRetry)); } var builder = new RetryDecoratorBuilder <TMessageType>(); configureRetry(builder); return(chainBuilder.AddDecorator(nextHandler => new RetryDecorator <TMessageType>(nextHandler, builder).HandleMessageAsync)); }
/// <summary> /// Handles exceptions /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The builder</param> /// <param name="exceptionHandlerFunc">The method that handles the exception. </param> /// <returns>The builder</returns> public static IChainBuilder <TMessageType> Exception <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Func <TMessageType, Exception, CancellationToken, Task> exceptionHandlerFunc) { if (exceptionHandlerFunc == null) { throw new ArgumentNullException(nameof(exceptionHandlerFunc)); } return(chainBuilder.AddDecorator( nextHandler => new ExceptionDecorator <TMessageType>( nextHandler, async(message, exception, token) => { await exceptionHandlerFunc(message, exception, token).ConfigureAwait(false); return false; }))); }
/// <summary> /// Handles exceptions /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The builder</param> /// <param name="exceptionHandlerAction">The method that handles the exception. </param> /// <returns>The builder</returns> public static IChainBuilder <TMessageType> Exception <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Action <TMessageType, Exception> exceptionHandlerAction) { if (exceptionHandlerAction == null) { throw new ArgumentNullException(nameof(exceptionHandlerAction)); } return(chainBuilder.AddDecorator( nextHandler => new ExceptionDecorator <TMessageType>( nextHandler, (message, exception, token) => { exceptionHandlerAction(message, exception); return FalseTask; }))); }
/// <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> Append <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Func <TMessageType, Task <TMessageType> > messageSelector) { if (messageSelector == null) { return(chainBuilder); } return(chainBuilder.AddDecorator( innerMessageHandler => { return async(message, token) => { #pragma warning disable CC0031 // Check for null before calling a delegate var chainedMessageTask = innerMessageHandler(message, token); #pragma warning restore CC0031 // Check for null before calling a delegate await Task.WhenAll(chainedMessageTask, InnerMessageHandlerAsync(innerMessageHandler, messageSelector, message, token)).ConfigureAwait(false); }; })); }
/// <summary> /// Prepend a message for each message passed through /// </summary> /// <typeparam name="TMessageType">The chain message type</typeparam> /// <param name="chainBuilder">The mch builder</param> /// <param name="messagePrependFunc">The function used to create the new message</param> /// <returns>The same mch builder</returns> public static IChainBuilder <TMessageType> Prepend <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Func <TMessageType, Task <TMessageType> > messagePrependFunc) { if (messagePrependFunc == null) { return(chainBuilder); } return(chainBuilder.AddDecorator( innerMessageHandler => { return (message, token) => { var prependMessageTask = InnerMessageHandlerAsync(innerMessageHandler, messagePrependFunc, message, token); #pragma warning disable CC0031 // Check for null before calling a delegate var chainedMessageTask = innerMessageHandler(message, token); #pragma warning restore CC0031 // Check for null before calling a delegate return Task.WhenAll(chainedMessageTask, prependMessageTask); }; })); }
/// <summary> /// Filters messages based on a predicate /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The builder</param> /// <param name="asyncPredicate">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, Task <bool> > asyncPredicate) { if (asyncPredicate == null) { return(chainBuilder); } return(chainBuilder.AddDecorator(innerMessageHandler => { return async(message, token) => { if (await asyncPredicate(message).ConfigureAwait(false)) { #pragma warning disable CC0031 // Check for null before calling a delegate await innerMessageHandler(message, token).ConfigureAwait(false); #pragma warning restore CC0031 // Check for null before calling a delegate } }; })); }
/// <summary> /// Prepend a message for each message passed through /// </summary> /// <typeparam name="TMessageType">The chain message type</typeparam> /// <param name="chainBuilder">The mch builder</param> /// <param name="messageAppendFunc">The function used to create the new message</param> /// <returns>The same mch builder</returns> public static IChainBuilder <TMessageType> Prepend <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Func <TMessageType, TMessageType> messageAppendFunc) { if (messageAppendFunc == null) { return(chainBuilder); } return(chainBuilder.AddDecorator( innerMessageHandler => { return (message, token) => { #pragma warning disable CC0031 // Check for null before calling a delegate var newMessageTask = innerMessageHandler(messageAppendFunc(message), token); var originalMessageTask = innerMessageHandler(message, token); #pragma warning restore CC0031 // Check for null before calling a delegate return Task.WhenAll(originalMessageTask, newMessageTask); }; })); }
/// <summary> /// Prevents the exception from propagating up the chain /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The builder</param> /// <returns>The builder</returns> public static IChainBuilder <TMessageType> IgnoreExceptions <TMessageType>(this IChainBuilder <TMessageType> chainBuilder) { return(chainBuilder.AddDecorator( innerMessageHandler => { return async(message, token) => { try { #pragma warning disable CC0031 // Check for null before calling a delegate await innerMessageHandler(message, token).ConfigureAwait(false); #pragma warning restore CC0031 // Check for null before calling a delegate } #pragma warning disable CC0004 // Catch block cannot be empty catch (Exception) { // ignored } #pragma warning restore CC0004 // Catch block cannot be empty }; })); }
/// <summary> /// Drops the the feedback chain, making the message handler chain fire and forget unless the message handler chain returns synchronously /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The message handler chain builder</param> /// <returns>A message handler chain builder</returns> public static IChainBuilder <TMessageType> SoftFireAndForget <TMessageType>(this IChainBuilder <TMessageType> chainBuilder) { return(chainBuilder.AddDecorator(nextHandler => new SoftFireAndForgetDecorator <TMessageType>(nextHandler).HandleMessageAsync)); }
/// <summary> /// Prevents more than once identical message from being handled concurrently. Messages not handled are dropped. /// If the message type is a value type, it's value is used. /// If the message type is a reference type, that instance of the message can not be handled concurrently /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The message handler chain builder</param> /// <returns>The message handler chain builder</returns> public static IChainBuilder <TMessageType> NoDuplicates <TMessageType>(this IChainBuilder <TMessageType> chainBuilder) { return(chainBuilder.AddDecorator(nextHandler => new NoDuplicatesDecorator <TMessageType, TMessageType>(nextHandler, m => m))); }
public static IChainBuilder <TMessageType> NoDuplicates <TMessageType, TKeyType>( this IChainBuilder <TMessageType> chainBuilder, Func <TMessageType, TKeyType> keySelector) { return(chainBuilder.AddDecorator(nextHandler => new NoDuplicatesDecorator <TMessageType, TKeyType>(nextHandler, keySelector))); }
/// <summary> /// Skips a fixed number of messages before allowing all messages to pass through /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The message handler chain builder</param> /// <param name="numberOfMessages">The number of messages to skip</param> /// <returns>A message handler chain builder</returns> public static IChainBuilder <TMessageType> Skip <TMessageType>(this IChainBuilder <TMessageType> chainBuilder, int numberOfMessages) { return(chainBuilder.AddDecorator(nextHandler => new SkipDecorator <TMessageType>(nextHandler, numberOfMessages))); }
/// <summary> /// Skips messages while the predicate returns true, then passes all through /// </summary> /// <typeparam name="TMessageType">The message type</typeparam> /// <param name="chainBuilder">The message handler chain builder</param> /// <param name="predicate">The predicate to determine whether to skip initial messages</param> /// <returns>A message handler chain builder</returns> public static IChainBuilder <TMessageType> SkipWhile <TMessageType>( this IChainBuilder <TMessageType> chainBuilder, Func <TMessageType, Task <bool> > predicate) { return(chainBuilder.AddDecorator(nextHandler => new SkipWhileAsyncDecorator <TMessageType>(nextHandler, predicate))); }
/// <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="timeToWait">The timespan to await</param> /// <returns>The builder</returns> public static IChainBuilder <TMessageType> Delay <TMessageType>(this IChainBuilder <TMessageType> chainBuilder, TimeSpan timeToWait) { return(chainBuilder.AddDecorator(nextHandler => new DelayDecorator <TMessageType>(nextHandler, timeToWait).HandleMessageAsync)); }
/// <summary> /// Parallelize the message throughput to X concurrent messages, dropping/breaking the feedback chain. /// </summary> /// <typeparam name="TMessageType">The message bus message type</typeparam> /// <param name="chainBuilder">The message handler chain builder</param> /// <param name="maxNumberOfConcurrentMessages">The maximum number of concurrent messages being handled (the level of parallelism)</param> /// <returns>The message handler chain builder used to stack more decorators</returns> public static IChainBuilder <TMessageType> ConcurrentFireAndForget <TMessageType>(this IChainBuilder <TMessageType> chainBuilder, int maxNumberOfConcurrentMessages) { return(chainBuilder.AddDecorator(nextHandler => new ConcurrentFireAndForgetDecorator <TMessageType>(nextHandler, maxNumberOfConcurrentMessages))); }