2013-03-04 55 views
4

我是DRY編碼的忠實粉絲,我喜歡儘可能避免使用鍋爐板代碼。因此,我將所有WCF頻道faff重構爲一個AOP類,它處理WCF頻道的生命週期。return await Method.Invoke()

我也是異步等待的狂熱粉絲,尤其是在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會有意想不到的結果。首先,捕獲FaultException將不會執行任何操作。其次,我會在請求返回之前關閉頻道。如果返回類型是Task,我理論上可以切換到另一個代碼路徑。但我無法弄清楚如何等待任務<>的結果,然後返回等待狀態。

這當然是特別困難的,因爲使用運行時AOP,我不能訪問能夠使用返回類型的泛型(沒有整個反射)。

任何想法如何實現這個函數作爲一個awaitable,它關閉完成通道和捕獲/編組異常到調用線程?

回答

6

要做async注射,您必須替換您返回的任務。爲了代碼可讀性,我建議將其替換爲async方法,而不是使用ContinueWith

我對fluentAOP並不熟悉,但我使用Castle DynamicProxy完成了async注入。

如果你想使用反射,你會想要做的是首先確定它是否是一個async呼叫(即,如果返回類型的子類或等於typeof(Task)。如果它是一個async呼叫,然後你需要使用反射來拉TTask<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; 
} 
+0

該死的。和我想的一樣。必須使用通用get方法。 – Aron 2013-03-04 14:01:53

+0

我正在玩'動態'功能,看看我是否可以進一步簡化這一點,但目前看起來並不樂觀。 – 2013-03-04 14:03:23

+0

不是動態的大粉絲,如果僅僅因爲你必須引用CSharp庫。無論如何,看起來像你的代碼是最好的...做一種希望C#對元編程有點更好。 – Aron 2013-03-05 03:06:15