Exemplo n.º 1
0
 /// <summary>
 /// Instantiates the class <see cref="MessageGateResult{TEnd}"/>
 /// </summary>
 /// <param name="result">The result kind.</param>
 /// <param name="endMessage">The end message.</param>
 /// <param name="exceptions">Exception messages.</param>
 public MessageGateResult(MessageGateResultKind result, TEnd endMessage, IEnumerable <ExceptionMessage> exceptions)
 {
     Result     = result;
     EndMessage = endMessage;
     Exceptions = exceptions;
 }
Exemplo n.º 2
0
        /// <summary>
        /// The method to send a start message and wait for the end message.
        /// </summary>
        /// <param name="startMessage">The start message.</param>
        /// <param name="onMessage">The action to send the message.</param>
        /// <param name="continueAction">The action to execute once a <see cref="MessageGateResult{TEnd}"/> was created.</param>
        /// <param name="timeout">
        /// Optionally a timeout after which the method will return, without sending the result.
        /// By default the timeout is <see cref="NoTimout"/>
        /// </param>
        /// <param name="cancellationToken">
        /// Optionally a cancellation token to cancel the continue operation. By default no CancellationToken will be used.
        /// </param>
        /// <remarks>
        /// <para>For an example how to use this class see the type documentation.</para>
        /// </remarks>
        public void SendAndContinue(TStart startMessage, Action <Message> onMessage, Action <MessageGateResult <TEnd> > continueAction, int timeout = NoTimout,
                                    CancellationToken cancellationToken = default)
        {
            if (startMessage == null)
            {
                throw new ArgumentNullException(nameof(startMessage));
            }

            if (onMessage == null)
            {
                throw new ArgumentNullException(nameof(onMessage));
            }

            if (continueAction == null)
            {
                throw new ArgumentNullException(nameof(continueAction));
            }

            CancellationToken userCancelToken      = cancellationToken;
            CancellationToken timeoutCancelToken   = default;
            List <CancellationTokenSource> sources = new List <CancellationTokenSource>();

            if (timeout != NoTimout)
            {
                CancellationTokenSource timeoutSource = new CancellationTokenSource(timeout);
                sources.Add(timeoutSource);
                timeoutCancelToken = timeoutSource.Token;
            }
            CancellationTokenSource combinedSource = CancellationTokenSource.CreateLinkedTokenSource(userCancelToken, timeoutCancelToken);

            cancellationToken = combinedSource.Token;
            sources.Add(combinedSource);

            MessageDomain.CreateNewDomainsFor(startMessage);
            CancellationTokenRegistration register = default;
            OneTimeAction action = new OneTimeAction(OnReceived);

            continueActions.TryAdd(startMessage.MessageDomain, action);
            register = cancellationToken.Register(OnCancel);

            onMessage(startMessage);

            void Dispose()
            {
                foreach (CancellationTokenSource tokenSource in sources)
                {
                    tokenSource.Dispose();
                }

                register.Dispose();
            }

            void OnReceived(Message message)
            {
                if (!continueActions.TryRemove(startMessage.MessageDomain, out _))
                {
                    return;
                }

                MessageDomain.TerminateDomainsOf(startMessage);
                if (message.TryGet(out TEnd endMessage))
                {
                    continueAction(new MessageGateResult <TEnd>(MessageGateResultKind.Success, endMessage,
                                                                Enumerable.Empty <ExceptionMessage>()));
                }
                else
                {
                    ExceptionMessage exceptionMessage = message.Get <ExceptionMessage>();
                    continueAction(
                        new MessageGateResult <TEnd>(MessageGateResultKind.Exception, null, new[] { exceptionMessage }));
                }

                Dispose();
            }

            void OnCancel()
            {
                if (!continueActions.TryRemove(startMessage.MessageDomain, out _))
                {
                    return;
                }

                MessageDomain.TerminateDomainsOf(startMessage);
                MessageGateResultKind resultKind = MessageGateResultKind.Success;

                if (userCancelToken.IsCancellationRequested)
                {
                    resultKind = MessageGateResultKind.Canceled;
                }
                else if (timeoutCancelToken.IsCancellationRequested)
                {
                    resultKind = MessageGateResultKind.Timeout;
                }

                MessageGateResult <TEnd> result =
                    new MessageGateResult <TEnd>(resultKind, null, Enumerable.Empty <ExceptionMessage>());

                continueAction(result);
                Dispose();
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// The method to send the start messages and wait for all end messages.
        /// </summary>
        /// <param name="startMessages">The start messages to send.</param>
        /// <param name="onMessage">The action to send the message.</param>
        /// <param name="onAggregated">The action to execute once a <see cref="MessageAggregationResult{TEnd}"/> was created.</param>
        /// <param name="timeout">
        /// Optionally a timeout after which the method will return, without sending the result.
        /// By default the timeout is <see cref="NoTimout"/>
        /// </param>
        /// <param name="cancellationToken">
        /// Optionally a cancellation token to cancel the continue operation. By default no CancellationToken will be used.
        /// </param>
        /// <remarks>
        /// <para>
        /// When a batch of messages is published using the this class they can be united again when all last messages of the execution chain are of the same type or an exception message.
        /// </para>
        /// <code>
        ///  --------------         ---------------------          -----------------
        /// | SplitMessage | ----> | IntermediateMessage | -----> | FinishedMessage |
        ///  --------------         ---------------------  |       -----------------
        ///                                                |
        ///                                                |       ------------------
        ///                                                *----> | ExceptionMessage |
        ///                                                |       ------------------
        ///                                                |
        ///                                                |       ------------------
        ///                                                 ----> | OtherEndMessage  |
        ///                                                        ------------------
        /// </code>
        /// <para>
        /// Looking at the example above it would not be possible to unite the <c>SplitMessages</c> again using this class as at least one <c>IntermediateMessage</c> let to an <c>OtherEndMessage</c>.
        /// </para>
        /// <para>
        /// This function is useful when the aggregated end messages need to be modified - for example
        /// filtered - before aggregating them. In all other cases it is better to use <see cref="SendAndAggregate"/>
        /// to automatically create and send an aggregated message.
        /// </para>
        /// <example>
        /// This is an example, how to use this method correctly:
        /// <code>
        /// [Consumes(typeof(FinishedMessage))]
        /// [Consumes(typeof(ExceptionMessage))]
        /// [Produces(typeof(StartMessage))]
        /// [Produces(typeof(AggregatedMessage))]
        /// public class MessageAggregatorAgent : Agent
        /// {
        ///     private readonly MessageGate&lt;FinishedMessage&gt; gate = new MessageGate&lt;FinishedMessage&gt;();
        ///
        ///     public MessageAggregatorAgent(IMessageBoard messageBoard) : base(messageBoard)
        ///     {
        ///     }
        ///
        ///     protected override void ExecuteCore(Message messageData)
        ///     {
        ///         if(gate.Check(messageData))
        ///         {
        ///             return;
        ///         }
        ///         //create startMessages
        ///         gate.SendAndContinue(startMessages, OnMessage, result =>
        ///         {
        ///             //manipulate the results and produce aggregated message
        ///             OnMessage(aggregatedMessage);
        ///         });
        ///     }
        /// }
        /// </code>
        /// </example>
        /// </remarks>
        public void SendAndContinue(IReadOnlyCollection <TStart> startMessages, Action <Message> onMessage,
                                    Action <MessageAggregationResult <TEnd> > onAggregated, int timeout = NoTimout,
                                    CancellationToken cancellationToken = default)
        {
            if (startMessages == null)
            {
                throw new ArgumentNullException(nameof(startMessages));
            }

            if (onMessage == null)
            {
                throw new ArgumentNullException(nameof(onMessage));
            }

            if (onAggregated == null)
            {
                throw new ArgumentNullException(nameof(onAggregated));
            }

            int aggregated = 0;
            ConcurrentBag <MessageStore <TEnd> >             endMessages          = new ConcurrentBag <MessageStore <TEnd> >();
            ConcurrentBag <MessageStore <ExceptionMessage> > aggregatedExceptions = new ConcurrentBag <MessageStore <ExceptionMessage> >();
            MessageGateResultKind resultKind = MessageGateResultKind.Success;

            foreach (TStart message in startMessages)
            {
                SendAndContinue(message, onMessage, AggregateResult, timeout, cancellationToken);
            }

            void AggregateResult(MessageGateResult <TEnd> messageGateResult)
            {
                if (messageGateResult.EndMessage != null)
                {
                    endMessages.Add(messageGateResult.EndMessage);
                }

                foreach (ExceptionMessage exception in messageGateResult.Exceptions)
                {
                    aggregatedExceptions.Add(exception);
                }

                if (messageGateResult.Result != MessageGateResultKind.Success)
                {
                    //It is ok to override other values with the latest
                    resultKind = messageGateResult.Result;
                }

                if (Interlocked.Increment(ref aggregated) == startMessages.Count)
                {
                    CompleteAggregation();
                }

                void CompleteAggregation()
                {
                    onAggregated(
                        new MessageAggregationResult <TEnd>(resultKind, endMessages.Select(s => (TEnd)s).ToArray(),
                                                            aggregatedExceptions.Select(s => (ExceptionMessage)s).ToArray()));
                    foreach (MessageStore <TEnd> store in endMessages)
                    {
                        store.Dispose();
                    }

                    foreach (MessageStore <ExceptionMessage> store in aggregatedExceptions)
                    {
                        store.Dispose();
                    }
                }
            }
        }
Exemplo n.º 4
0
 /// <summary>
 /// Instantiate a new instance of <see cref="MessageAggregationResult{TEnd}"/>.
 /// </summary>
 /// <param name="result">The aggregation result.</param>
 /// <param name="endMessages">The final messages.</param>
 /// <param name="exceptions">Exceptions during execution.</param>
 public MessageAggregationResult(MessageGateResultKind result, IReadOnlyCollection <TEnd> endMessages, IReadOnlyCollection <ExceptionMessage> exceptions)
 {
     Result      = result;
     EndMessages = endMessages;
     Exceptions  = exceptions;
 }