2012-04-04 9 views
4

Von dem, was ich über die Async CTP mit dem asynchronen Ereignismuster gesehen habe, sollte der Code, den ich hier habe, gut funktionieren, mit var result1 = await tcs1.Task blockiert bis clientGetFileList.GetCompleted feuert. Was jedoch passiert, ist, dass ich zurück zu GetRestoreStream geprallt bin, unter return GetRestoreStreamAwait().Result, die niemals zurückkommt - stattdessen stört mich meine App ziemlich.Verwendung von async/await mit Live SDK

Kann mir bitte jemand erklären, was ich falsch mache?

protected override Stream GetRestoreStream() 
{ 
    if (SkyDriveFolderId != null) 
     return GetRestoreStreamAwait().Result; 

    return Stream.Null; 
} 

private async Task<Stream> GetRestoreStreamAwait() 
{ 
    LiveConnectClient clientGetFileList = new LiveConnectClient(_session); 
    TaskCompletionSource<LiveOperationCompletedEventArgs> tcs1 = new TaskCompletionSource<LiveOperationCompletedEventArgs>(); 
    EventHandler<LiveOperationCompletedEventArgs> d1 = (o, e) => { tcs1.TrySetResult(e); }; 

    clientGetFileList.GetCompleted += d1; 
    clientGetFileList.GetAsync(SkyDriveFolderId + "/files"); 
    var result1 = await tcs1.Task; 
    clientGetFileList.GetCompleted -= d1; 

    // ... method continues for a while 
} 

Update: Dieses Stück Code scheint durch die ganze Art und Weise zu bewegen, aber task.Start() weg wirft ein InvalidOperationException so dass ich eigentlich nie den Strom am Ende bekommen. Wenn man es in einen try/catch wickelt, ändert sich auch nichts - ohne die try/catch wird die InvalidOperationException weiter oben im Stack abgefangen, während der Vorgang glücklicherweise ohne Wissen darüber abläuft, dass sein Ergebnis niemals verwendet wird; mit ihm task.Result friert die Dinge genauso sicher wie der Code oben.

protected override Stream GetRestoreStream() 
{ 
    if (SkyDriveFolderId != null) 
    { 
     var task = GetRestoreStreamImpl(); 
     task.Start(); 
     return task.Result; 
    } 

    return Stream.Null; 
} 

private async Task<Stream> GetRestoreStreamImpl() 
{ 
    var getResult = await GetTaskAsync(SkyDriveFolderId + "/files"); 

    List<object> data = (List<object>)getResult["data"]; 
    foreach (IDictionary<string, object> dictionary in data) 
    { 
     if (dictionary.ContainsKey("name") && (string)dictionary["name"] == BackupFileName) 
     { 
      if (dictionary.ContainsKey("id")) 
      { 
       SkyDriveFileId = (string)dictionary["id"]; 
       break; 
      } 
     } 
    } 

    if (String.IsNullOrEmpty(SkyDriveFileId)) 
    { 
     MessageBox.Show("Restore failed: could not find backup file", "Backup", MessageBoxButton.OK); 
     return Stream.Null; 
    } 

    return await DownloadTaskAsync(SkyDriveFileId + "/content"); 
} 

private Task<IDictionary<string,object>> GetTaskAsync(string path) 
{ 
    var client = new LiveConnectClient(_session); 
    var tcs = new TaskCompletionSource<IDictionary<string, object>>(); 

    client.GetCompleted += (o, e) => 
     { 
      if (e.Error != null) 
       tcs.TrySetException(e.Error); 
      else if (e.Cancelled) 
       tcs.TrySetCanceled(); 
      else 
       tcs.TrySetResult(e.Result); 
     }; 
    client.GetAsync(path); 
    return tcs.Task; 
} 

private Task<Stream> DownloadTaskAsync(string path) 
{ 
    var client = new LiveConnectClient(_session); 
    var tcs = new TaskCompletionSource<Stream>(); 

    client.DownloadCompleted += (o, e) => 
     { 
      if (e.Error != null) 
       tcs.TrySetException(e.Error); 
      else if (e.Cancelled) 
       tcs.TrySetCanceled(); 
      else 
       tcs.TrySetResult(e.Result); 
     }; 
    client.DownloadAsync(path); 
    return tcs.Task; 
} 
+0

(wenn es in der Tat durch die Zeit, die await genannt wird abgeschlossen ist) es scheint, dass Microsoft bereits ein 'async' /' erwarten' Beispiel für das Live Connect SDK und dass mein Google-fu etwas Arbeit braucht. Das Beispiel befindet sich unter https://github.com/liveservices/LiveSDK/tree/master/Samples/WindowsPhone/LiveSDKAsyncAwaitSample und funktioniert etwas anders als das, was ich versuchte. –

Antwort

3

Sie missverstehen die Art und Weise, async/await funktioniert. Grundsätzlich blockiert Ihr Code bei var result1 und darunter. Was erwartet wird, ist jedoch, dass der Code, der die asynchrone Methode aufgerufen hat (GetRestoreStream in diesem Fall) zurückgegeben wird, sobald eine lange laufende Aufgabe * mit await davor aufgerufen wird. Wenn Sie nicht auf .Result angewiesen wären, würde Ihre GetRestoreStream-Methode abgeschlossen sein. Da Sie jedoch das Ergebnis benötigen, wird Ihre GetRestoreStream-Methode synchron, während sie auf GetRestoreStreamAwait wartet. Ich werde in Kürze ein paar Bilder hinzufügen.

Hier einige Beispiel-Code Fluss:

-GetRestoreStream calls GetRestoreStreamAwait 
---GetRestoreStreamAwait calls an async task 
-GetRestoreStreamAwait returns to GetRestoreStream with a pending result 
-GetRestoreStream can do anything it wants, but if it calls for the pending result, it will block 
---GetRestoreStreamAwait finally finishes its async task and continues through its code, returning a result 
-Any code in GetRestoreStream that was waiting for the result receives the Result 

Dies ist nicht die beste grafische Darstellung, hoffentlich hilft es, es wenn auch ein wenig zu erklären. Die Sache zu beachten ist, dass der Code-Fluss ist nicht das, was Sie aufgrund der Natur der asynchronen

sind Also, meine Vermutung ist, dass Ihre App nur abstürzt, weil Sie versuchen, auf die Result zuzugreifen, die noch nicht verfügbar ist und alles, was Sie tun müssen, ist warten auf die tcs1.Task abzuschließen. Wenn Sie das Sperren vermeiden möchten, müssen Sie den Aufruf verschachteln, sodass GetRestoreStream ebenfalls eine asynchrone Methode ist. Wenn das Ergebnis jedoch das ist, was Sie letztendlich suchen, müssen Sie auf die Rückgabe warten oder einfach einen Rückruf wie für das asynchrone Muster einrichten.

* Beachten Sie, dass ich lange andauernde Aufgabe weil der Compiler verschwenden Zeit Umschreiben Code nicht, der bereits abgeschlossen ist

UPDATE ... versuchen diese

protected override Stream GetRestoreStream() 
{ 
    if (SkyDriveFolderId != null) 
     return GetRestoreStreamAwait().Result; 

    return Stream.Null; 
} 

private async Task<Stream> GetRestoreStreamAwait() 
{ 

    try 
    { 
    LiveConnectClient clientGetFileList = new LiveConnectClient(_session); 
    TaskCompletionSource<LiveOperationCompletedEventArgs> tcs1 = new TaskCompletionSource<LiveOperationCompletedEventArgs>(); 
    EventHandler<LiveOperationCompletedEventArgs> d1 = 
     (o, e) => 
      { 
       try 
       { 
        tcs1.TrySetResult(e); 
       } 
       catch(Exception ex) 
       { 
        tcs1.TrySetResult(null); 
       } 
      }; 

    clientGetFileList.GetCompleted += d1; 
    clientGetFileList.GetAsync(SkyDriveFolderId + "/files"); 
    var result1 = await tcs1.Task; 
    clientGetFileList.GetCompleted -= d1; 

    // ... method continues for a while 
    } 
    catch(Exception ex) 
    { 
     return null; 
    } 
} 
+0

Nach dem, was ich gelesen habe, sollen Aufgabenobjekte "heiß" zurückgegeben werden und eine Ausnahme auslösen, wenn "Start" für sie mehr als einmal aufgerufen wird. Außerdem versuche ich, eine Kette von asynchronen EAP-Methoden zu einer synchronen zu machen, weil die Elternklasse für die, an der ich arbeite, erforderlich ist. –

+0

Die Sache ist, nicht mehr, wie lange ich alles dort sitzen lasse, diese erste asynchrone Aufgabe scheint nie zu Ende zu sein.Ich komme nie zu dem Punkt, an dem 'result1' gefüllt und' d1' ausgehakt wird. Ich denke, das wäre auch bei der nächsten Veranstaltung der Fall, aber ich bin noch nie zu diesem Punkt gekommen, egal wie lange ich den Emulator und Debugger laufen lasse. –

+0

@ChrisCharabaruk Ich bin nicht so vertraut mit dem TrySetResult. Ich weiß, dass es so benutzt werden kann, wie du es tust, aber ich kann mir nicht sicher sein. Können Sie versuchen, den Code in eine Aufgabe für ein reguläres Get zu wickeln und zu sehen, ob es zurückkommt? Meine Vermutung ist, dass dieser asynchrone Anruf einfach nicht richtig konfiguriert ist und sich dreht, bis er eine Rückkehr erhält .... was er nie tun wird. Vielleicht wirft ein regulärer get einen Fehler? –