2015-01-22 16 views
21

Meine Fragen kehrt auf diesen Beitrag im Zusammenhang Intercept the call to an async method using DynamicProxyIntercept Asynchron-Methode, die allgemeine Aufgabe <> über Dynamic

ich Abfangjäger implementieren möchten, die mit Asynchron-Methoden arbeitet, die Task oder Task<T> Ergebnis zurückgibt.

verwende ich nächsten Code für die Rückkehr ContinueWith Ergebnis (in der Reihenfolge, dass Anrufer Methode warten Sie, während Abfangjäger Arbeit beendet)

var task = invocation.ReturnValue as Task; 
invocation.ReturnValue = task.ContinueWith(c => 
     { code that should execute after method finish }); 

obigen Code funktioniert für Task Ergebnis in Ordnung, aber im Fall von Task<T> Ergebnis ContinueWith wird Rückgabetyp ändern von Task<T> bis Task. Ich brauche überladene Methode ContinueWith aufzurufen, die Task<T> zurückgibt, aber dafür muss ich invocation.ReturnValue zu Task<T>

ich so, wie es in irgendeiner Weise dynamisch werfen nicht das finden werfen. Weiß jemand, wie man es macht?

Ich habe auch versucht, diese Methode über Reflexion zu nennen, aber Parameter ist labmda-Funktion, die nicht direkt übergeben werden kann.

+1

Check out https://msdn.microsoft.com/en-us/magazine/dn574805.aspx - Artikel ist für Unity Interzeptoren, aber "wickeln Sie die Aufgabe" Code hängt nicht von Ihrem DI-Container. –

Antwort

18

Nach umfangreichen Recherchen konnte ich eine Lösung erstellen, die zum Abfangen von synchronen Methoden sowie Async Task und Async Task < TResult> funktioniert.

Hier ist mein Code für eine Ausnahmebehandlung Interceptor, die auf alle diese Methodentypen arbeitet mit Castle Dynamic Proxy. Dieses Muster ist anpassbar für jede Art von Intercept, die Sie wünschen. Die Syntax wird für standardmäßige BearInvoke-/AfterInvoke-Aktionen ein wenig sauberer sein, aber das Konzept sollte dasselbe sein.

(andere. Hinweis: Die IExceptionHandler Schnittstelle im Beispiel ist ein benutzerdefinierter Typ und kein gemeinsames Objekt)

private class AsyncExceptionHandlingInterceptor : IInterceptor 
    { 
     private static readonly MethodInfo handleAsyncMethodInfo = typeof(AsyncExceptionHandlingInterceptor).GetMethod("HandleAsyncWithResult", BindingFlags.Instance | BindingFlags.NonPublic); 
     private readonly IExceptionHandler _handler; 

     public AsyncExceptionHandlingInterceptor(IExceptionHandler handler) 
     { 
      _handler = handler; 
     } 

     public void Intercept(IInvocation invocation) 
     { 
      var delegateType = GetDelegateType(invocation); 
      if (delegateType == MethodType.Synchronous) 
      { 
       _handler.HandleExceptions(() => invocation.Proceed()); 
      } 
      if (delegateType == MethodType.AsyncAction) 
      { 
       invocation.Proceed(); 
       invocation.ReturnValue = HandleAsync((Task)invocation.ReturnValue); 
      } 
      if (delegateType == MethodType.AsyncFunction) 
      { 
       invocation.Proceed(); 
       ExecuteHandleAsyncWithResultUsingReflection(invocation); 
      } 
     } 

     private void ExecuteHandleAsyncWithResultUsingReflection(IInvocation invocation) 
     { 
      var resultType = invocation.Method.ReturnType.GetGenericArguments()[0]; 
      var mi = handleAsyncMethodInfo.MakeGenericMethod(resultType); 
      invocation.ReturnValue = mi.Invoke(this, new[] { invocation.ReturnValue }); 
     } 

     private async Task HandleAsync(Task task) 
     { 
      await _handler.HandleExceptions(async() => await task); 
     } 

     private async Task<T> HandleAsyncWithResult<T>(Task<T> task) 
     { 
      return await _handler.HandleExceptions(async() => await task); 
     } 

     private MethodType GetDelegateType(IInvocation invocation) 
     { 
      var returnType = invocation.Method.ReturnType; 
      if (returnType == typeof(Task)) 
       return MethodType.AsyncAction; 
      if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) 
       return MethodType.AsyncFunction; 
      return MethodType.Synchronous; 
     } 

     private enum MethodType 
     { 
      Synchronous, 
      AsyncAction, 
      AsyncFunction 
     } 
    } 
+1

Danke für die Bereitstellung, es war genau das, was ich gesucht habe. Nur so ist es für andere klar, hier ist die IExceptionHandler-Schnittstelle Methoden.In meiner Implementierung habe ich diese Klassen umbenannt, um generisch zu sein, um sie in jedem Interceptor zu verwenden, der asynchrone Methoden abfangen kann. 'void Handle (Aktion synchronousInvoker)', 'Task Handle (Func wartebarInvoker)', 'Task Handle (Func > swarfableInvoker)'. –

+0

Ich sehe, dass 'HandleAsyncWithResult' Methode nie aufgerufen wird. Hast du etwas im Beispielcode vergessen? Ich sehe, dass im Falle von 'AsyncFunction' Ausnahmen nicht behandelt werden. –

+0

Die Methode 'HandleAsyncWithResult' wird auf Umwegen von der Methode 'ExecuteHandleAsyncWithResultUsingReflection' aufgerufen. Die Ausnahmen werden von den .NET-Aufgabenobjekten behandelt und beim Aufruf korrekt ausgelöst. –

6

Eine bessere Lösung, um das dynamic Schlüsselwort zu verwenden wäre, die Compiler Typprüfung zu umgehen und lösen die Operation zur Laufzeit:

public void Intercept(IInvocation invocation) 
{ 
    invocation.Proceed(); 
    var method = invocation.MethodInvocationTarget; 
    var isAsync = method.GetCustomAttribute(typeof(AsyncStateMachineAttribute)) != null; 
    if (isAsync && typeof(Task).IsAssignableFrom(method.ReturnType)) 
    { 
     invocation.ReturnValue = InterceptAsync((dynamic)invocation.ReturnValue); 
    } 
} 

private static async Task InterceptAsync(Task task) 
{ 
    await task.ConfigureAwait(false); 
    // do the continuation work for Task... 
} 

private static async Task<T> InterceptAsync<T>(Task<T> task) 
{ 
    T result = await task.ConfigureAwait(false); 
    // do the continuation work for Task<T>... 
    return result; 
} 
+1

Dies ist eine viel prägnantere Lösung! – iamyojimbo

+1

Danke, das hat mein Problem gelöst! – jparaya

1

Bedarf Mit Methoden abfangen Rückkehr Task<TResult>, ich habe eine Erweiterung Castle.Core erstellt, die den Prozess vereinfacht.

Castle.Core.AsyncInterceptor

Das Paket ist auf NuGet zum Download zur Verfügung.

Die Lösung basiert weitgehend auf der Antwort von @silas-reinagel, vereinfacht sie aber durch Bereitstellung einer neuen Schnittstelle zur Implementierung IAsyncInterceptor. Es gibt auch weitere Abstraktionen, die das Abfangen ähnlich der Implementierung von Interceptor machen.

Weitere Details finden Sie im Projekt readme des Projekts.