it-swarm.com.de

Verwenden Sie effektiv async / await mit der ASP.NET-Web-API

Ich versuche, die Funktion async/await Von ASP.NET in meinem Web-API-Projekt zu verwenden. Ich bin mir nicht sicher, ob dies die Leistung meines Web-API-Dienstes beeinflussen wird. Nachfolgend finden Sie den Workflow und den Beispielcode aus meiner Anwendung.

Arbeitsablauf:

Benutzeroberflächenanwendung → Web-API-Endpunkt (Controller) → Methode in der Web-API-Service-Schicht aufrufen → Einen anderen externen Webdienst aufrufen. (Hier haben wir die DB-Interaktionen usw.)

Controller:

public async Task<IHttpActionResult> GetCountries()
{
    var allCountrys = await CountryDataService.ReturnAllCountries();

    if (allCountrys.Success)
    {
        return Ok(allCountrys.Domain);
    }

    return InternalServerError();
}

Service Layer:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

    return Task.FromResult(response);
}

Ich habe den obigen Code getestet und arbeite. Ich bin mir aber nicht sicher, ob es sich um die korrekte Verwendung von async/await Handelt. Bitte teilen Sie Ihre Gedanken.

98
arp

Ich bin mir nicht sicher, ob es einen Unterschied in der Leistung meiner API macht.

Bedenken Sie, dass der Hauptvorteil von asynchronem Code auf der Serverseite Skalierbarkeit ist. Es wird Ihre Anfragen nicht auf magische Weise beschleunigen. In meinem Artikel zu async ASP.NET beschreibe ich verschiedene Aspekte, die ich beachten sollte, wenn ich async verwende.

Ich denke, Ihr Anwendungsfall (Aufrufen anderer APIs) eignet sich gut für asynchronen Code. Denken Sie jedoch daran, dass "asynchron" nicht "schneller" bedeutet. Der beste Ansatz ist, zuerst Ihre [~ # ~] ui [~ # ~] reaktionsschnell und asynchron zu machen; Dadurch wird Ihre App feel schneller, auch wenn sie etwas langsamer ist.

Soweit der Code geht, ist dies nicht asynchron:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
  var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
  return Task.FromResult(response);
}

Sie benötigen eine wirklich asynchrone Implementierung, um die Skalierbarkeitsvorteile von async nutzen zu können:

public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

Oder (wenn Ihre Logik in dieser Methode wirklich nur ein Durchgang ist):

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

Beachten Sie, dass es einfacher ist, von innen nach außen zu arbeiten, als von außen nach innen. Mit anderen Worten, beginnen Sie nicht mit einer asynchronen Controller-Aktion und erzwingen Sie anschließend die Asynchronität nachgelagerter Methoden. Identifizieren Sie stattdessen die natürlich asynchronen Vorgänge (Aufrufen externer APIs, Datenbankabfragen usw.) und stellen Sie diese auf der Ebene niedrigste zuerst (Service.ProcessAsync). Lassen Sie dann das async hochrinnen und machen Sie Ihre Controller-Aktionen als letzten Schritt asynchron.

Und auf keinen Fall sollten Sie Task.Run In diesem Szenario.

171
Stephen Cleary

Es ist richtig, aber vielleicht nicht nützlich.

Da es nichts zu warten gibt - keine Aufrufe zum Blockieren von APIs, die asynchron arbeiten könnten -, richten Sie Strukturen ein, um asynchrone Vorgänge (die Overhead verursachen) zu verfolgen, ohne diese Funktion zu nutzen.

Wenn die Service-Schicht beispielsweise DB-Operationen mit Entity Framework ausführte, das asynchrone Aufrufe unterstützt:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    using (db = myDBContext.Get()) {
      var list = await db.Countries.Where(condition).ToListAsync();

       return list;
    }
}

Sie würden dem Worker-Thread erlauben, etwas anderes zu tun, während die Datenbank abgefragt wurde (und somit in der Lage ist, eine andere Anfrage zu verarbeiten).

Das Warten ist in der Regel etwas, das bis zum Ende gehen muss: Es ist sehr schwierig, es in ein vorhandenes System nachzurüsten.

9
Richard

Sie nutzen Async/Warten nicht effektiv, da der Anforderungsthread während der Ausführung der synchronen Methode ReturnAllCountries() blockiert wird.

Der Thread, der für die Verarbeitung einer Anforderung zugewiesen ist, wartet im Leerlauf, während ReturnAllCountries() seine Arbeit erledigt.

Wenn Sie ReturnAllCountries() asynchron implementieren können, sehen Sie Vorteile für die Skalierbarkeit. Dies liegt daran, dass der Thread möglicherweise zurück in den .NET-Threadpool freigegeben wird, um eine andere Anforderung zu verarbeiten, während ReturnAllCountries() ausgeführt wird. Dies würde Ihrem Service einen höheren Durchsatz ermöglichen, indem Threads effizienter genutzt werden.

0
James Wierzba