2016-11-21 9 views
0

Wir arbeiten an einem Projekt, das in UWP (Frontend) und REST-MVC-IIS (Backend) entwickelt wurde.UWP + IIS + asynchrones Verhalten

Ich war auf einem theoretischen Szenario denken, die sich daraus ergeben können:

Von dem, was ich weiß, gibt es keine Möglichkeit, die Reihenfolge, in der gewährleisten Anfragen werden von IIS verarbeitet und serviert werden.

So in einem einfachen Szenario, lassen Sie uns dies einfach mal davon aus:

UI:

Selection (productId = 1);

SelectionChanged (productId = 2);

private async void SelectionChanged(int productId) 
{ 
    await GetProductDataAsync(productId); 
} 

IIS:

GetProductDataAsync(productId=1) scheduled on thread pool 

GetProductDataAsync(productId=2) scheduled on thread pool 

GetProductDataAsync(productId=2) finishes first => send response to client 

GetProductDataAsync(productId=1) finishes later => send response to client 

Wie Sie sehen können, beendet der Antrag auf productId=2 aus irgendeinem Grund schneller als die erste Anfrage für productId=1.

Da die Funktionsweise von async funktioniert, erstellen beide Aufrufe zwei Fortsetzungsaufgaben auf der Benutzeroberfläche, die sich gegenseitig überschreiben, wenn sie nicht in der richtigen Reihenfolge angezeigt werden, da sie die gleichen Daten enthalten.

Dies kann auf fast jedes Master-Detail-Szenario extrapoliert werden, wo es passieren kann am Ende ein Master-Element auswählen und die falschen Details dafür bekommen (wegen der Reihenfolge, in der die Antwort von IIS kommt). Ich wollte wissen, ob es einige Best Practices gibt, um diese Art von Szenarien zu meistern ... viele Lösungen kommen mir in den Sinn, aber ich möchte nicht auf die Idee kommen und eine Implementierung durchführen, bevor ich versuche Sehen Sie, welche anderen Optionen auf dem Tisch liegen.

+1

Die beiden UI ruft Sie zeigen; Sind sie buchstäblich 2 Zeilen Code hintereinander? Wenn dies der Fall ist, beginnt die zweite Zeile erst mit der Ausführung, wenn die erste abgeschlossen ist, und der gesamte Umlauf nach IIS ist abgeschlossen. – sellotape

+0

Nun, Sie haben den Async verpasst, der für die zwei aufeinander folgenden Aufrufe verwendet wird, der alles ändert, da sie nicht sequenziell, sondern parallel verarbeitet werden. Nun, nicht parallel je nach Aussage, aber das Ergebnis dieser beiden Aufrufe wird später in einer Fortsetzungsaufgabe für beide Aufrufe verarbeitet, was bedeutet, dass der erste Aufruf, der zurückkehrt, zuerst verarbeitet wird, wenn die zweite Anfrage zuerst beendet wird (aus welchem ​​Grund auch immer) dann landen Sie in der oben beschriebenen Situation, wo der spätere Aufruf das Ergebnis des früheren Aufrufs überschreibt. –

+1

Wenn diese 2 UI-Zeilen fortlaufend sind, werden sie nicht parallel verarbeitet. Das Warten bedeutet genau das: Warten Sie, bis der Anruf beendet ist, bevor Sie mit der nächsten Zeile fortfahren. Die Ausführung wird möglicherweise in der aufrufenden Methode fortgesetzt, wird jedoch in der aktuellen Methode erst fortgesetzt, wenn die erwartete Aufgabe abgeschlossen ist. – sellotape

Antwort

0

Wie Sie Ihren Code präsentiert await GetProductDataAsync(productId=2); wird immer ausgeführt, nachdem await GetProductDataAsync(productId=1); abgeschlossen hat. Also, es gibt keine Wettlaufbedingung.

Wenn Ihr Code ist:

await Task.WhenAll(
    GetProductDataAsync(productId=1), 
    GetProductDataAsync(productId=2)) 

Dann könnte es eine Race-Bedingung sein. Und wenn das ein Problem ist, ist es nicht speziell auf async-await, sondern aufgrund der Tatsache, dass Sie gleichzeitige Anrufe tätigen.

Wenn Sie diesen Code in einem anderen Verfahren wickeln und ConfigureAwait() verwenden, werden Sie nur eine Fortsetzung auf dem UI-Thread haben:

Task GetProductDataAsync() 
{ 
    await Task.WhenAll(
     GetProductDataAsync(productId=1).ConfigureAwait(), 
     GetProductDataAsync(productId=2).ConfigureAwait() 
    ).ConfigureAwait(); 
} 
+0

Ich habe meinen ursprünglichen Beitrag bearbeitet, ich habe die Situation besser erklärt. Es geht nicht um Race Conditions, es geht um die Reihenfolge, in der die Callbacks nach der Bearbeitung von den Iis zurückkommen. –

0

Ich glaube, ich bekommen, was Sie sagen. Aufgrund des asynchronen void eventhandlers wartet nichts in der UI auf den ersten Aufruf vor dem zweiten. Ich stelle mir ein Drop-Down von Werten vor und wenn es sich ändert, holt es die relevanten Daten.

Im Idealfall möchten Sie wahrscheinlich die Benutzeroberfläche während des Anrufs sperren oder eine cancellationtoken implementieren.

Wenn Sie nur nach einer Möglichkeit suchen, die Anrufe zu messen, lesen Sie weiter ...

Ich verwende eine Singleton-Repository-Schicht in der UWP-Anwendung, die behandelt, ob die Daten von einem Web-Service oder einer lokal zwischengespeicherten Kopie abgerufen werden. Wenn Sie die Anforderungen für die Verarbeitung einzeln verarbeiten möchten, verwenden Sie SemaphoreSlim. Es funktioniert wie Sperre, aber für asynchrone Operationen (vereinfachtes Gleichnis). Hier

ist ein Beispiel, das veranschaulichen soll, wie es funktioniert ...

public class ProductRepository : IProductRepository 
{ 
    //initializing (1,1) will allow only 1 use of the object 
    static SemaphoreSlim semaphoreLock = new SemaphoreSlim(1, 1); 
    public async Task<IProductData> GetProductDataByIdAsync(int productId) 
    { 
     try 
     { 
      //if semaphore is in use, subsequent requests will wait here 
      await semaphoreLock.WaitAsync(); 
      try 
      { 
       using (var client = new HttpClient()) 
       { 
        client.BaseAddress = new Uri("yourbaseurl"); 
        client.DefaultRequestHeaders.Accept.Clear(); 
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 
        string url = "yourendpoint"; 
        HttpResponseMessage response = await client.GetAsync(url); 
        if (response.IsSuccessStatusCode) 
        { 
         var json = await response.Content.ReadAsStringAsync(); 
         ProductData prodData = JsonConvert.DeserializeObject<ProductData>(json); 
         return prodData; 
        } 
        else 
        { 
         //handle non-success 
        } 
       } 
      } 
      catch (Exception e) 
      { 
       //handle exception 
      } 
     } 
     finally 
     { 
      //if any requests queued up, the next one will fire here 
      semaphoreLock.Release(); 
     } 
    } 
} 
+0

Danke, du warst der einzige, der das Problem verstanden hat :) wollte dir nur Anerkennung dafür geben! –

Verwandte Themen