2017-12-12 3 views
2

So habe ich den folgenden Programmablauf mit asynchronen Methoden in einer ASP.NET-Web-API-Anwendung: ein API-Controller ruft eine asynchrone Methode (nennen wir es asyncMethod1), die etwas von einer zweiten asynchronen Methode benötigt (nennen wir es asyncMethod2). Der API-Controller muss nicht wirklich wissen, wann asyncMethod1 beendet ist, obwohl es nett wäre, dem Benutzer Feedback zu geben. asyncMethod1 muss auf der Hand wissen, wann asyncMethod2 beendet ist. HierSo rufen Sie eine asynchrone Methode von einer anderen in ASP.NET-Web-API-Anwendung

ist der entsprechende Code:

public class MyController : ApiController 
{ 
    private CustomClass customClass; 

    public IHttpActionResult WorkMethod(TestObject testObject) 
    { 
     Debug.WriteLine("WorkMethod - before calling asyncMethod1"); 

     Task.Run(async() => await this.asyncMethod1(testObject)).Wait(); // ERROR HERE 

     Debug.WriteLine("WorkMethod - after calling asyncMethod1"); 

     return Ok(); 
    } 

    private async Task asyncMethod1(TestObject testObject) 
    { 
     Debug.WriteLine("asyncMethod1 - before calling asyncMethod2"); 

     Dictionary<string, string> x = await this.asyncMethod2(testObject); 

     Debug.WriteLine("asyncMethod1 - after calling asyncMethod2"); 

     try 
     { 
      using (Task<IList<TestResponse>> testTask = customClass.DoAsyncWork1(x)) 
      { 
       IList<TestResponse> response = await testTask; 

       // Do something with response 
       Debug.WriteLine("Transform 'response'"); 
      } 
     } 
     catch (Exception ex) 
     { 
      throw new Exception(" Exception ", ex); 
     } 
    } 

    private async Task<Dictionary<string, string>> asyncMethod2(TestObject testObject) 
    { 
     try 
     { 
      Debug.WriteLine("asyncMethod2 - before calling DoAsyncWork2"); 

      using (Task<IList<SomeData>> workTask = customClass.DoAsyncWork2(testObject.x)) 
      { 
       IList<SomeData> data = await workTask ; 

       var output = new Dictionary<string, string>(); 
       output = data.values; 

       Debug.WriteLine("asyncMethod2 - returning data to caller"); 

       return output; 
      } 
     } 
     catch (Exception ex) 
     { 
      throw new Exception(" Exception ", ex); 
     } 
    } 
} 

Custom ist eine Klasse in einem benutzerdefinierten API Ich verwende, wobei Methoden DoAsyncWork1 und DoAsyncWork2 beide async sind.

Wenn der Code oben läuft, habe ich die folgende Ausnahme in Verfahren erhalten Arbeits- methode, Linie Task.Run(async() => await this.asyncMethod1(testObject)).Wait();:

"Message": "Ein Fehler ist aufgetreten.", "ExceptionMessage": "Ein oder mehrere Fehler aufgetreten. "," ExceptionType ":" System.AggregateException "," StackTrace ":" um
System.Threading.Tasks.Task.ThrowIfExceptional (Boolean includeTaskCanceledExceptions)
bei System.Threading.Tasks.Task.Wait (Int32 MillisekundenTimeout , CancellationToken cancellationToken)
bei System.Threading.Tasks.Task.Wait()
bei MyCont roller.WorkMethod (Test testObject) in MyController.cs: Zeile 9
bei Lambda_method (Closure, Object, Object [])
bei System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor. <> c__DisplayClass10.b__9 (Objektinstanz, Object [] methodParameters)
bei System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute (Objektinstanz, Object [] Argumente)
bei System.Web.Http.Controllers.ReflectedHttpActionDescriptor. ExecuteAsync (HttpControllerContext Controller, IDictionary`2 Argumente, CancellationToken CancellationToken)

Und der Ausgang ist die folgende:

Arbeits- methode - vor asyncMethod1 Aufruf
asyncMethod1 - vor asyncMethod2
asyncMethod2 Aufruf - vor DoAsyncWork2
asyncMethod2 Aufruf - Daten Rückkehr
asyncMethod1 an Anrufer - nach asyncMethod2

Wenn im Aufruf der Methode Arbeits- methode ich das ändern:

Task.Run(async() => await this.asyncMethod1(asyncMethod1)).Wait(); 

dazu:

var task = this.asyncMethod1(asyncMethod1); 

Debug.WriteLine("WorkMethod - after calling asyncMethod1"); 

task.wait(); 

I don bekomme keine Ausnahme, aber der Programmablauf kommt nie an den Punkt in asyncMethod1, wo es etwas Arbeit macht über die Antwort von asyncMethod2 zurückgegeben.Darüber hinaus ist der Ausgang die folgenden:

Arbeits- methode - vor asyncMethod1
asyncMethod1 Aufruf - vor asyncMethod2
asyncMethod2 Aufruf - vor DoAsyncWork2
Arbeits- methode aufrufen - nach asyncMethod1 Aufruf

Was ist das Problem hier? Was ist der richtige Weg, um das zu erreichen, was ich erreichen möchte, d. H. Eine Async-Methode von einem API-Controller aufrufen und dann eine zweite Async-Methode von der ersten aufrufen?

+0

Sie sollten niemals bei Async-Aufrufen mit 'Wait()' oder '.Result' blockieren. Warum ist 'WorkMethod' nicht asynchron? Welche Arbeit wird in 'Task.Run()' erledigt? Sie verschwenden einen Thread, wenn die Arbeit speziell IO gebunden ist. – Crowcoder

+0

@Crowcoder Ich habe keine besonders gute Antwort darauf, abgesehen von den folgenden Beispielen, die ich gefunden habe, wo eine nicht-asynchrone Methode eine asynchrone Methode aufruft. Ist es zwingend erforderlich, dass eine Methode, die eine asynchrone Methode aufruft, auch asynchron ist? Irgendwann in der Kette musst du eine nicht-asynchrone Methode haben, richtig ?! Sie können nicht alle asynchron sein. Wie für Ihre zweite Frage ist die Arbeit erledigt Task.Run() ist asyncMethod1 aufrufen. – lukegf

+0

In einer Web-API sollte es einfach sein, alles async zu halten, da es die Clients, die es aufrufen, nicht beeinflusst. Wenn Sie nicht alles asynchron machen können, ist es besser, alles synchron zu machen, da das Mischen der beiden zu einer ineffizienten Ressourcennutzung und einem Deadlock-Risiko führt. Ich sehe es 'asyncMethod1' genannt, aber es ist offensichtlich nicht ausgearbeitet, so dass ich nicht weiß, was Sie in dieser Methode wirklich tun werden. – Crowcoder

Antwort

4

sagte Sie in Ihrem Kommentar:

An einem gewissen Punkt auf der Kette, können Sie eine nicht-Asynchron-Methode haben haben, richtig ?!

Die Antwort darauf ist nein. Alles kann asynchron sein. So können Sie dies als Ihre Aktion haben:

Dann macht diese Art von macht diese Methode sinnlos. Sie können Ihre asyncMethod1 die tatsächliche Aktionsmethode machen. Wenn du willst.

Das sagte, die Ausnahme, die Sie erhielten, ist, weil es tatsächlich eine Ausnahme gibt. Aber weil Sie nicht das Ergebnis waren, wird es als AggregateException mit der echten Ausnahme innerhalb der InnerExceptions Sammlung geworfen. Sobald Sie die Aufgabe await haben, sehen Sie die echte Ausnahme geworfen.

+0

Sie haben Recht. Sobald ich sehen konnte, dass die echte Ausnahme ausgelöst wurde, erkannte ich, dass das Problem nicht in meinem Code, sondern in der benutzerdefinierten API, in der die Klasse CustomClass lebte, lag. Sobald das behoben war, begannen die Dinge gut zu funktionieren. – lukegf

Verwandte Themen