我是DRY编码的忠实粉丝,我喜欢尽可能避免锅炉板代码。因此,我将所有WCF频道faff重构为AOP类,该类处理WCF频道的生命周期。
我也是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合同的情况下
[ServiceContract]
public interface IFoo
{
[OperationContract]
Task<int> GetInt();
}
GetInt会有意想不到的结果。首先,catchException将无效。其次,我会在请求返回之前关闭通道。如果返回类型是Task,我理论上可以切换到另一个代码路径。但我无法弄清楚如何等待任务的结果&lt;&gt;然后返回一个等待的。
这当然特别困难,因为运行时AOP我无法访问能够使用返回类型的泛型(没有完整的反射)。
任何想法如何实现这个函数作为一个等待,它关闭完成的通道和捕获/编组调用线程的异常?
答案 0 :(得分:7)
要进行async
注射,您必须更换已退回的任务。为了代码可读性,我建议使用async
方法替换它,而不是使用ContinueWith
。
我不熟悉fluentAOP,但我已经使用Castle DynamicProxy注入了async
。
如果你想使用反射,你要做的是首先确定它是否是async
调用(即,如果返回类型是typeof(Task)
的子类或等于async
。如果是T
来电,那么您需要使用反射从Task<T>
中提取async
并将其应用于您自己的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;
}
}
方法:
dynamic
另一种方法是使用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;
}
:
{{1}}