是否可以使用Castle.DynamicProxy创建异步交互?

Eri*_*ken 5 asynchronous castle-dynamicproxy

我们基本上有一个如下所示的类,它使用Castle.DynamicProxy进行拦截.

using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Castle.DynamicProxy;

namespace SaaS.Core.IoC
{
    public abstract class AsyncInterceptor : IInterceptor
    {
        private readonly ILog _logger;

        private readonly ConcurrentDictionary<Type, Func<Task, IInvocation, Task>> wrapperCreators =
            new ConcurrentDictionary<Type, Func<Task, IInvocation, Task>>();

        protected AsyncInterceptor(ILog logger)
        {
            _logger = logger;
        }

        void IInterceptor.Intercept(IInvocation invocation)
        {
            if (!typeof(Task).IsAssignableFrom(invocation.Method.ReturnType))
            {
                InterceptSync(invocation);
                return;
            }

            try
            {
                CheckCurrentSyncronizationContext();
                var method = invocation.Method;

                if ((method != null) && typeof(Task).IsAssignableFrom(method.ReturnType))
                {
                    var taskWrapper = GetWrapperCreator(method.ReturnType);
                    Task.Factory.StartNew(
                        async () => { await InterceptAsync(invocation, taskWrapper).ConfigureAwait(true); }
                        , // this will use current synchronization context
                        CancellationToken.None,
                        TaskCreationOptions.AttachedToParent,
                        TaskScheduler.FromCurrentSynchronizationContext()).Wait();
                }
            }
            catch (Exception ex)
            {
                //this is not really burring the exception
                //excepiton is going back in the invocation.ReturnValue which 
                //is a Task that failed. with the same excpetion 
                //as ex.
            }
        }
....
Run Code Online (Sandbox Code Playgroud)

最初这段代码是:

Task.Run(async () => { await InterceptAsync(invocation, taskWrapper)).Wait()
Run Code Online (Sandbox Code Playgroud)

但是在调用它之后我们丢失了HttpContext,所以我们不得不将它切换到:

Task.Factory.StartNew 
Run Code Online (Sandbox Code Playgroud)

所以我们可以传入TaskScheduler.FromCurrentSynchronizationContext()

所有这些都很糟糕,因为我们实际上只是将一个线程换成另一个线程.我真的很想改变签名

void IInterceptor.Intercept(IInvocation invocation)
Run Code Online (Sandbox Code Playgroud)

async Task IInterceptor.Intercept(IInvocation invocation)
Run Code Online (Sandbox Code Playgroud)

并摆脱Task.Run或Task.Factory,并使其成为:

await InterceptAsync(invocation, taskWrapper);
Run Code Online (Sandbox Code Playgroud)

问题是Castle.DynamicProxy IInterecptor不允许这样做.我真的想在拦截中做一个等待.我可以做.Result但是那时我调用的异步调用的重点是什么?如果没有能够做到等待,我就失去了能够产生这个线程执行的好处.我没有被Castle Windsor用于他们的DynamicProxy,所以我正在寻找另一种方法来做到这一点.我们已经研究过Unity,但我不想替换我们的整个AutoFac实现.

任何帮助,将不胜感激.

Ste*_*ary 6

所有这些都很糟糕,因为我们实际上只是将一个线程换成另一个线程.

真正.另外因为StartNew版本实际上并没有等待方法完成; 它只会等到第一个await.但是如果你添加一个Unwrap()让它等待完整的方法,那么我强烈怀疑你最终会遇到死锁.

问题是Castle.DynamicProxy IInterecptor不允许这样做.

IInterceptor确实有一个必须同步进行的设计限制.因此,这限制了您的拦截功能:您可以在异步方法之前或之后注入同步代码,并在异步方法之后注入异步代码.在异步方法之前无法注入异步代码.这只是DynamicProxy的一个限制,一个非常痛苦的纠正(如打破所有现有的用户代码).

做各种注塑说的支持的,你必须改变你的思考了一下.其中一个有效的心理模型asyncTask从方法返回的代表该方法的执行.因此,要将代码附加到该方法,您可以直接调用该方法,然后使用扩充的方法替换任务返回值.

所以,像这样的东西(对于返回类型Task):

protected abstract void PreIntercept(); // must be sync
protected abstract Task PostInterceptAsync(); // may be sync or async

// This method will complete when PostInterceptAsync completes.
private async Task InterceptAsync(Task originalTask)
{
  // Asynchronously wait for the original task to complete
  await originalTask;

  // Asynchronous post-execution
  await PostInterceptAsync();
}

public void Intercept(IInvocation invocation)
{
  // Run the pre-interception code.
  PreIntercept();

  // *Start* the intercepted asynchronous method.
  invocation.Proceed();

  // Replace the return value so that it only completes when the post-interception code is complete.
  invocation.ReturnValue = InterceptAsync((Task)invocation.ReturnValue);
}
Run Code Online (Sandbox Code Playgroud)

请注意,PreIntercept截获的方法,PostInterceptAsync都在原始(ASP.NET)上下文中运行.

PS快速谷歌搜索异步DynamicProxy导致了这一点.不过,我不知道它有多稳定.