我是 DRY 编码的忠实拥护者,我喜欢尽可能避免样板代码。因此,我将我所有的 WCF channel faff 重构为一个 AOP 类,它处理 WCF channel 的生命周期。
我也是 async-await 的忠实拥护者,尤其是 WCF,因为理论上它会释放通常会休眠等待响应的线程。
所以我在fluentAOP lib中创建了一个拦截器
private static object InvokeOnChannel(IMethodInvocation methodInvocation)
{
var proxy = _factory.CreateChannel();
var channel = (IChannel) proxy;
try
{
channel.Open();
var ret = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments);
channel.Close();
return ret;
}
catch (FaultException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
catch(Exception)
{
channel.Abort();
throw;
}
}
但是,在仔细考虑解决方案时,我注意到在表单的 WCF 契约(Contract)的情况下
[ServiceContract]
public interface IFoo
{
[OperationContract]
Task<int> GetInt();
}
GetInt 会产生意想不到的结果。首先,catch FaultException 什么都不做。其次,我会在请求返回之前关闭 channel 。如果返回类型是 Task,理论上我可以切换到另一个代码路径。但是我不知道如何等待 Task<> 的结果然后返回可等待的结果。
这当然特别困难,因为使用运行时 AOP 我将无法访问能够使用返回类型的泛型(没有整个反射)。
关于如何将此函数实现为可等待对象的任何想法,它会在完成时关闭 channel 并捕获/编码(marshal)调用线程的异常?
最佳答案
要执行 async
注入(inject),您必须替换返回的任务。为了代码的可读性,我建议将其替换为 async
方法,而不是使用 ContinueWith
。
我对 fluentAOP 不熟悉,但是我用 CaSTLe DynamicProxy 完成了 async
注入(inject)。
如果你想使用反射,你要做的是首先确定它是否是一个 async
调用(即,如果返回类型是 typeof(Task)
的子类或等于 async
。如果它是一个 T
调用,那么你将需要使用反射从 Task<T>
中提取 async
并将其应用于您自己的 dynamic
方法:
private static MethodInfo handleAsync = ...; // point this to HandleAsync<T>
// Only called if the return type is Task/Task<T>
private static object InvokeAsyncOnChannel(IMethodInvocation methodInvocation)
{
var proxy = _factory.CreateChannel();
var channel = (IChannel) proxy;
try
{
channel.Open();
var task = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments) as Task;
object ret;
if (task.GetType() == typeof(Task))
ret = HandleAsync(task, channel);
else
ret = handleAsync.MakeGenericMethod(task.GetType().GetGenericParameters()).Invoke(this, task, channel);
return ret;
}
catch (FaultException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
catch(Exception)
{
channel.Abort();
throw;
}
}
private static async Task HandleAsync(Task task, IChannel channel)
{
try
{
await task;
channel.Close();
}
catch (FaultException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
catch(Exception)
{
channel.Abort();
throw;
}
}
private static async Task<T> HandleAsync<T>(Task task, IChannel channel)
{
try
{
var ret = await (Task<T>)task;
channel.Close();
return ret;
}
catch (FaultException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
catch(Exception)
{
channel.Abort();
throw;
}
}
另一种方法是使用 ojit_code :
private static object InvokeOnChannel(IMethodInvocation methodInvocation)
{
var proxy = _factory.CreateChannel();
var channel = (IChannel) proxy;
try
{
channel.Open();
dynamic result = methodInvocation.Method.Invoke(proxy, methodInvocation.Arguments);
return Handle(result, channel);
}
catch (FaultException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
catch(Exception)
{
channel.Abort();
throw;
}
}
private static async Task Handle(Task task, IChannel channel)
{
try
{
await task;
channel.Close();
}
catch (FaultException ex)
{
if (ex.InnerException != null)
throw ex.InnerException;
throw;
}
catch(Exception)
{
channel.Abort();
throw;
}
}
private static async Task<T> Handle<T>(Task<T> task, IChannel channel)
{
await Handle((Task)task, channel);
return await task;
}
private static T Handle<T>(T result, IChannel channel)
{
channel.Close();
return result;
}
关于c# - 返回 await Method.Invoke(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15199447/