示例#1
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="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 wait operation. This is helpful, when for example the
        /// imitated service call is an async method. By default no CancellationToken will be used.
        /// </param>
        /// <returns>The <see cref="MessageGateResult{TEnd}"/> of the operation.</returns>
        /// <remarks>
        /// <para>For an example how to use this class see the type documentation.</para>
        /// <para>
        /// WARNING: Extensive use of this method will lead to time gaps in the execution. See .net issue on github: https://github.com/dotnet/runtime/issues/55562
        /// Use this method only for the legacy service call. Of all other scenarios use <see cref="SendAndContinue(TStart,System.Action{Agents.Net.Message},System.Action{Agents.Net.MessageGateResult{TEnd}},int,System.Threading.CancellationToken)"/>.
        /// </para>
        /// </remarks>
        public MessageGateResult <TEnd> SendAndAwait(TStart startMessage, Action <Message> onMessage, int timeout = NoTimout,
                                                     CancellationToken cancellationToken = default)
        {
            if (startMessage == null)
            {
                throw new ArgumentNullException(nameof(startMessage));
            }

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

            using (ManualResetEventSlim resetEvent = new ManualResetEventSlim())
            {
                ManualResetEventSlim     local  = resetEvent;
                MessageGateResult <TEnd> result = null;
                SendAndContinue(startMessage, onMessage, r =>
                {
                    result = r;
                    local.Set();
                }, timeout, cancellationToken);

                //Do not use cancellation token here. The this is handled in the SendAndContinueMethod
                resetEvent.Wait();
                return(result);
            }
        }
示例#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();
            }
        }