2012-12-31 11 views
23

Mam webservice napisane w Yii (framework php).Jak korzystać z asynchronizacji/oczekiwać na połączenie z serwisem internetowym?

Używam C# i Visual Studio 2012 do opracowania aplikacji WP8. Dodałem odwołanie do usługi do mojego projektu (Add Service Reference). Dzięki temu mogę korzystać z funkcji serwisu internetowego.

client = new YChatWebService.WebServiceControllerPortTypeClient(); 

    client.loginCompleted += client_loginCompleted; // this.token = e.Result; 
    client.loginAsync(this.username, this.password); 

    client.getTestCompleted += client_getTestCompleted; 
    client.getTestAsync(this.token); 

funkcja getTestAsync i loginAsync powrotu void i oba są asynchroniczne. Czy funkcje mogą powrócić Task<T>? Chciałbym użyć w moim programie słów kluczowych: async/await.

Odpowiedź:

Dziękuję za pomoc.

Poniższy kod wydaje się działać.

internal static class Extension 
    { 
     private static void TransferCompletion<T>(
      TaskCompletionSource<T> tcs, System.ComponentModel.AsyncCompletedEventArgs e, 
    Func<T> getResult) 
     { 
      if (e.Error != null) 
      { 
       tcs.TrySetException(e.Error); 
      } 
      else if (e.Cancelled) 
      { 
       tcs.TrySetCanceled(); 
      } 
      else 
      { 
       tcs.TrySetResult(getResult()); 
      } 
     } 

     public static Task<loginCompletedEventArgs> LoginAsyncTask(this YChatWebService.WebServiceControllerPortTypeClient client, string userName, string password) 
     { 
      var tcs = new TaskCompletionSource<loginCompletedEventArgs>(); 
      client.loginCompleted += (s, e) => TransferCompletion(tcs, e,() => e); 
      client.loginAsync(userName, password); 
      return tcs.Task; 
     } 
    } 

nazywam to w ten sposób

 client = new YChatWebService.WebServiceControllerPortTypeClient(); 
     var login = await client.LoginAsyncTask(this.username, this.password); 
+0

Po prostu zmień typ zwrotu nieważności na Zadanie, następnie możesz zadzwonić z oczekiwaniem na klienta.loginAsync (this.username, this.password); –

+1

@NickBray A potem to nie zadziała, ponieważ nie będziesz właściwie zwracał zadania ukończonego we właściwym czasie ... – Servy

+0

http://msdn.microsoft.com/en-us/library/hh524395.aspx –

Odpowiedz

28

Zakładając, że loginAsync zwraca void i Zdarzenie loginCmpleted gdy logowanie jest zrobione, to się nazywa asynchroniczny Wzór zdarzeń opartych lub EAP.

Aby przekonwertować EAP na oczekujące/asynchroniczne, należy skonsultować się z Tasks and the Event-based Asynchronous Pattern. W szczególności będziesz chciał skorzystać z TaskCompletionSource, aby przekonwertować model oparty na zdarzeniach na model oparty na zadaniach. Gdy masz już model oparty na zadaniach, możesz użyć funkcji oczekującej na C# 5.

Oto przykład:

// Use LoginCompletedEventArgs, or whatever type you need out of the .loginCompleted event 
// This is an extension method, and needs to be placed in a static class. 
public static Task<LoginCompletedEventArgs> LoginAsyncTask(this YChatWebService.WebServiceControllerPortTypeClient client, string userName, string password) 
{ 
    var tcs = CreateSource<LoginCompletedEventArgs>(null); 
    client.loginCompleted += (sender, e) => TransferCompletion(tcs, e,() => e, null); 
    client.loginAsync(userName, password); 
    return tcs.Task; 
} 

private static TaskCompletionSource<T> CreateSource<T>(object state) 
{ 
    return new TaskCompletionSource<T>( 
     state, TaskCreationOptions.None); 
} 

private static void TransferCompletion<T>( 
    TaskCompletionSource<T> tcs, AsyncCompletedEventArgs e, 
    Func<T> getResult, Action unregisterHandler) 
{ 
    if (e.UserState == tcs) 
    { 
     if (e.Cancelled) tcs.TrySetCanceled(); 
     else if (e.Error != null) tcs.TrySetException(e.Error); 
     else tcs.TrySetResult(getResult()); 
     if (unregisterHandler != null) unregisterHandler(); 
    } 
} 

Teraz, gdy już konwertowane programowania asynchronicznego modelu zdarzeń opartego na jedno zadanie opartym można teraz używać czekają:

var client = new YChatWebService.WebServiceControllerPortTypeClient(); 
var login = await client.LoginAsyncTask("myUserName", "myPassword"); 
+0

Dziękuję. Musiałem zapoznać się z terminy, takie jak wyrażenia lambda, delegaci ... "Tak więc wszystko jest dla mnie bardzo nowe.Kompilator ma problem z" e => e ", więc zmieniłem go na"() => e "i nie ma opcji TaskCreationOptions. Oddzielne z. Co powinienem zamiast tego użyć? – MPeli

+0

TaskCreateOptions.None powinno być OK, tak, mój błąd, powinien być() => e, lub() => e.Foo, jakakolwiek właściwość chcesz tam ściągnąć. Zaktualizuję kod: –

+0

@ Meli Jeśli ta odpowiedź rozwiązała dla ciebie, proszę oznaczyć ją jako odpowiedź Dziękuję –

7

podczas dodawania odniesienie usług upewnij się, że wybrany Generate Task based operations w Advanced sekcji. to stworzy awaitable metod takich jak LoginAsync powrocie Task<string>

+0

Używa modelu opartego na zdarzeniu, więc 'OdAsync' nie pomoże. – Servy

+0

@Servy Użyłem adresu URL w pytaniu i wygenerowałem automatycznie ważne funkcje. Nic złego? – I4V

+0

Nie mogę wybrać opcji Generuj operacje oparte na zadaniu, ponieważ są wyszarzone. Wydaje się, że jest wyłączony dla projektów WP8. Zobacz [ten temat] (http://stackoverflow.com/questions/13266079/wp8-sdk-import-service-reference-with-task-based -operations-not-possible) – MPeli

-3

Jeśli chcą mieć możliwość oczekiwania na metody, powinni zwrócić zadanie. Nie możesz czekać na metodę, która zwraca pustkę. Jeśli chcesz, aby zwracali wartość, np. Int powinni zwrócić Task<int>, wtedy metoda powinna zwrócić int.

public async Task loginAsync(string username, string password) {} 

Następnie można wywołać

Task t = loginAsync(username, password); 
//login executing 
//do something while waiting 

await t; //wait for login to complete 
+0

Pyta * jak * to zrobić. Nie wie, w jaki sposób model oparty na zdarzeniach może zwrócić zadanie. – Servy

+0

Zastosuj tę samą metodę, którą masz, i po prostu ustaw zadanie zamiast nieważne. Następnie możesz zadzwonić oczekując na swój program. Bez dodatkowej pracy. Jak go używać to inna historia. –

+0

Nie można po prostu zmienić metody zwracania metody i należy to zrobić. Pytanie brzmi w szczególności, jak zmienić implementację metody tak, aby rzeczywiście zwracała zadanie. Pytanie brzmi, jak utworzyć "zadanie", które jest wykonywane, gdy dane są gotowe, a ty nie odpowiadasz. Zobacz odpowiedź Judy, aby dowiedzieć się, w jaki sposób ją realizujesz. – Servy

3

miałem to zrobić kilka razy w ciągu ostatniego roku i Używałem zarówno @ kodu Judy powyżej i original example on odwoływać ale za każdym razem mam problem z obu: połączenie asynchroniczne działa, ale nie kończy. Jeśli przejdę przez to, zobaczę, że wejdzie w metodę TransferCompletion, ale e.UserState == tcs zawsze będzie false.

Okazuje się, że metody asynchronizacji usług internetowych, takie jak OP loginAsync, mają dwie sygnatury.Drugi akceptuje parametr userState. Rozwiązaniem jest przekazanie obiektu TaskCompletionSource<T> utworzonego jako ten parametr. W ten sposób e.UserState == tcs zwróci wartość true.

W OP, e.UserState == tcs został usunięty, aby kod działał, co jest zrozumiałe - ja też byłem kuszony. Sądzę jednak, że jest to możliwe, aby zapewnić zakończenie właściwego zdarzenia.

pełny kod jest:

public static Task<LoginCompletedEventArgs> RaiseInvoiceAsync(this Client client, string userName, string password) 
{ 
    var tcs = CreateSource<LoginCompletedEventArgs>(); 
    LoginCompletedEventHandler handler = null; 
    handler = (sender, e) => TransferCompletion(tcs, e,() => e,() => client.LoginCompleted -= handler); 
    client.LoginCompleted += handler; 

    try 
    { 
     client.LoginAsync(userName, password, tcs); 
    } 
    catch 
    { 
     client.LoginCompleted -= handler; 
     tcs.TrySetCanceled(); 
     throw; 
    } 

    return tcs.Task; 
} 

Alternatywnie, wierzę, że jest to nieruchomość tcs.Task.AsyncState też, że zapewni userState. Więc można zrobić coś takiego:

if (e.UserState == taskCompletionSource || e.UserState == taskCompletionSource?.Task.AsyncState) 
{ 
    if (e.Cancelled) taskCompletionSource.TrySetCanceled(); 
    else if (e.Error != null) taskCompletionSource.TrySetException(e.Error); 
    else taskCompletionSource.TrySetResult(getResult()); 
    unregisterHandler(); 
} 

To właśnie próbowałem początkowo jako wydawało lżejsze podejście i mogę przekazać Guid zamiast pełnego obiektu TaskCompletionSource. Stephen Cleary ma numer good write-up of the AsyncState, jeśli jesteś zainteresowany.

Powiązane problemy