예제 #1
0
        /// <summary>
        /// Wraps a <see cref="Task"/> into the Begin method of an APM pattern.
        /// </summary>
        /// <param name="task">The task to wrap.</param>
        /// <param name="callback">The callback method passed into the Begin method of the APM pattern.</param>
        /// <param name="descriptor">The state passed into the Begin method of the APM pattern.</param>
        /// <returns>The asynchronous operation, to be returned by the Begin method of the APM pattern.</returns>
        protected IAsyncResult BeginInvoke(Task task, AsyncCallback callback, ConsumerDescriptor descriptor)
        {
            var tcs = new TaskCompletionSource <object>(descriptor, TaskCreationOptions.RunContinuationsAsynchronously);

            // "_ =" to discard 'async/await' compiler warning
            _ = AwaitCompletionAsync(task, callback, tcs, descriptor);

            return(tcs.Task);
        }
예제 #2
0
        private void HandleException(Exception ex, ConsumerDescriptor descriptor)
        {
            if (ex is AggregateException ae)
            {
                ae.InnerExceptions.Each(x => Logger.Error(x));
            }
            else
            {
                Logger.Error(ex);
            }

            if (!descriptor.FireForget)
            {
                throw ex;
            }
        }
예제 #3
0
 protected internal virtual IEnumerable <object> ResolveParameters(
     IComponentContext container,
     ConsumerDescriptor descriptor,
     CancellationToken cancelToken)
 {
     foreach (var p in descriptor.Parameters)
     {
         if (p.ParameterType == typeof(CancellationToken))
         {
             yield return(cancelToken);
         }
         else
         {
             yield return(_resolver.ResolveParameter(p, container));
         }
     }
 }
예제 #4
0
 private async Task AwaitCompletionAsync(
     Task task,
     AsyncCallback callback,
     TaskCompletionSource <object> tcs,
     ConsumerDescriptor descriptor)
 {
     try
     {
         await task;
         tcs.TrySetResult(null);
     }
     catch (OperationCanceledException ex)
     {
         tcs.TrySetCanceled(ex.CancellationToken);
     }
     catch (Exception ex)
     {
         tcs.TrySetException(ex);
     }
     finally
     {
         callback?.Invoke(tcs.Task);
     }
 }
예제 #5
0
        public void Invoke <TMessage>(ConsumerDescriptor descriptor, IConsumer consumer, ConsumeContext <TMessage> envelope) where TMessage : class
        {
            var d           = descriptor;
            var p           = descriptor.WithEnvelope ? (object)envelope : envelope.Message;
            var fastInvoker = FastInvoker.GetInvoker(d.Method);

            if (!d.FireForget && !d.IsAsync)
            {
                // The all synch case
                try
                {
                    InvokeCore();
                }
                catch (Exception ex)
                {
                    HandleException(ex, d);
                }
            }
            else if (!d.FireForget && d.IsAsync)
            {
                //// The awaitable Task case
                //BeginInvoke((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken), EndInvoke, d);

                // For now we must go with the AsyncRunner, the above call to BeginInvoke (APM > TPL) does not always
                // guarantee that the task is awaited and throws exceptions especially when EF is involved.
                using (var runner = AsyncRunner.Create())
                {
                    try
                    {
                        runner.Run((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken));
                    }
                    catch (Exception ex)
                    {
                        HandleException(ex, d);
                    }
                }
            }
            else if (d.FireForget && !d.IsAsync)
            {
                // A synch method should be executed async (without awaiting)
                AsyncRunner.Run((c, ct, state) => InvokeCore(c, ct), d)
                .ContinueWith(t => EndInvoke(t), /*TaskContinuationOptions.OnlyOnFaulted*/ TaskContinuationOptions.None)
                .ConfigureAwait(false);
            }
            else if (d.FireForget && d.IsAsync)
            {
                // An async (Task) method should be executed without awaiting
                AsyncRunner.Run((c, ct) => (Task)InvokeCore(c, ct), d)
                .ContinueWith(t => EndInvoke(t), TaskContinuationOptions.OnlyOnFaulted)
                .ConfigureAwait(false);
            }

            object InvokeCore(IComponentContext c = null, CancellationToken cancelToken = default(CancellationToken))
            {
                if (d.Parameters.Length == 0)
                {
                    // Only one method param: the message!
                    return(fastInvoker.Invoke(consumer, p));
                }

                var parameters = new object[d.Parameters.Length + 1];

                parameters[0] = p;

                int i = 0;

                foreach (var obj in ResolveParameters(c, d, cancelToken).ToArray())
                {
                    i++;
                    parameters[i] = obj;
                }

                return(fastInvoker.Invoke(consumer, parameters));
            }
        }
예제 #6
0
        public void Invoke <TMessage>(ConsumerDescriptor descriptor, IConsumer consumer, ConsumeContext <TMessage> envelope) where TMessage : class
        {
            var d           = descriptor;
            var p           = descriptor.WithEnvelope ? (object)envelope : envelope.Message;
            var fastInvoker = FastInvoker.GetInvoker(d.Method);

            var items = HttpContext.Current.GetItem("ConsumerInvoker", () => new List <ConsumerDescriptor>());

            items.Add(d);

            if (!d.FireForget && !d.IsAsync)
            {
                // The all synch case
                try
                {
                    InvokeCore();
                }
                catch (Exception ex)
                {
                    HandleException(ex, d);
                }
            }
            else if (!d.FireForget && d.IsAsync)
            {
                // The awaitable Task case
                BeginInvoke((Task)InvokeCore(cancelToken: AsyncRunner.AppShutdownCancellationToken), EndInvoke, d);
            }
            else if (d.FireForget && !d.IsAsync)
            {
                // A synch method should be executed async (without awaiting)
                AsyncRunner.Run((c, ct, state) => InvokeCore(c, ct), d)
                .ContinueWith(t => EndInvoke(t), /*TaskContinuationOptions.OnlyOnFaulted*/ TaskContinuationOptions.None)
                .ConfigureAwait(false);
            }
            else if (d.FireForget && d.IsAsync)
            {
                // An async (Task) method should be executed without awaiting
                AsyncRunner.Run((c, ct) => (Task)InvokeCore(c, ct), d)
                .ContinueWith(t => EndInvoke(t), TaskContinuationOptions.OnlyOnFaulted)
                .ConfigureAwait(false);
            }

            object InvokeCore(IComponentContext c = null, CancellationToken cancelToken = default(CancellationToken))
            {
                if (d.Parameters.Length == 0)
                {
                    // Only one method param: the message!
                    return(fastInvoker.Invoke(consumer, p));
                }

                var parameters = new object[d.Parameters.Length + 1];

                parameters[0] = p;

                int i = 0;

                foreach (var obj in ResolveParameters(c, d, cancelToken).ToArray())
                {
                    i++;
                    parameters[i] = obj;
                }

                return(fastInvoker.Invoke(consumer, parameters));
            }
        }