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实现.
任何帮助,将不胜感激.
所有这些都很糟糕,因为我们实际上只是将一个线程换成另一个线程.
真正.另外因为StartNew
版本实际上并没有等待方法完成; 它只会等到第一个await
.但是如果你添加一个Unwrap()
让它等待完整的方法,那么我强烈怀疑你最终会遇到死锁.
问题是Castle.DynamicProxy IInterecptor不允许这样做.
IInterceptor
确实有一个必须同步进行的设计限制.因此,这限制了您的拦截功能:您可以在异步方法之前或之后注入同步代码,并在异步方法之后注入异步代码.在异步方法之前无法注入异步代码.这只是DynamicProxy的一个限制,一个非常痛苦的纠正(如打破所有现有的用户代码).
做各种注塑说的是支持的,你必须改变你的思考了一下.其中一个有效的心理模型async
是Task
从方法返回的代表该方法的执行.因此,要将代码附加到该方法,您可以直接调用该方法,然后使用扩充的方法替换任务返回值.
所以,像这样的东西(对于返回类型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导致了这一点.不过,我不知道它有多稳定.
归档时间: |
|
查看次数: |
1349 次 |
最近记录: |