2012-12-31 15 views
23

मेरे पास Yii (PHP ढांचे) में लिखा गया है।मैं webservice को कॉल करने के लिए async/प्रतीक्षा का उपयोग कैसे कर सकता हूं?

मैं एक WP8 अनुप्रयोग विकसित करने के लिए सी # और विजुअल स्टूडियो 2012 का उपयोग करता हूं। मैंने अपनी परियोजना के लिए एक सेवा संदर्भ जोड़ा (सेवा संदर्भ जोड़ें)। तो मैं webservice कार्यों का उपयोग करने में सक्षम हूँ।

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); 

समारोह getTestAsync और loginAsync वापसी void और दोनों अतुल्यकालिक कर रहे हैं। क्या कार्यों के लिए Task<T> वापस करना संभव है? मैं अपने प्रोग्राम में async/await कीवर्ड का उपयोग करना चाहता हूं।

उत्तर:

आपकी मदद के लिए धन्यवाद।

निम्नलिखित कोड काम करने लगता है।

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; 
     } 
    } 

मैं आपकी सेवा संदर्भ जोड़ने हालांकि यह इस तरह से

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

बस शून्य रिटर्न प्रकार को कार्य में बदलें, फिर आप ग्राहक का इंतजार कर सकते हैं .loginAsync (this.username, this.password); –

+1

@ निकब्रे और फिर यह काम नहीं करेगा क्योंकि आप वास्तव में एक कार्य वापस नहीं कर पाएंगे जो उचित समय पर पूरा हो गया है ... – Servy

+0

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

उत्तर

28

यह मानते हुए कि loginAsync शून्य देता है, और loginCmpleted घटना पर सक्रिय होता है लॉगिन किया जाता है LoginAsync तरह awaitable तरीकों पैदा करेगा, इस घटना के आधार पर अतुल्यकालिक पैटर्न, या EAP कहा जाता है।

ईएपी को प्रतीक्षा/एसिंक में बदलने के लिए, Tasks and the Event-based Asynchronous Pattern से परामर्श लें। विशेष रूप से, आप ईवेंट-आधारित मॉडल को कार्य-आधारित मॉडल में कनवर्ट करने के लिए TaskCompletionSource का उपयोग करना चाहेंगे। एक बार जब आप कार्य-आधारित मॉडल प्राप्त कर लेते हैं, तो आप सी # 5 की सेक्सी प्रतीक्षा सुविधा का उपयोग कर सकते हैं।

यहाँ एक उदाहरण है: अब जब कि तुम एक टास्क के आधार पर एक के लिए इवेंट आधारित async प्रोग्रामिंग मॉडल परिवर्तित कर दिया है

// 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(); 
    } 
} 

, अब आप उपयोग कर सकते हैं का इंतजार है:

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

धन्यवाद। मुझे परिचित होना था लैम्ब्डा अभिव्यक्तियों जैसे प्रतिनिधियों, प्रतिनिधियों ... "तो सब कुछ मेरे लिए बहुत नया है। कंपाइलर में" ई => ई "के साथ समस्या है इसलिए मैंने इसे"() => ई "में बदल दिया और कोई विकल्प टास्कक्रिएशनऑप्शन नहीं है। DetachedFromParent। इसके बजाय मुझे क्या उपयोग करना चाहिए? – MPeli

+0

टास्कक्रेटऑप्शन। कोई भी ठीक नहीं होना चाहिए। हाँ, मेरी गलती, यह होना चाहिए() => ई, या() => ई.फू, जो भी संपत्ति आप वहां खींचना चाहते हैं। कोड को अपडेट कर देगा। –

+0

@MPeli अगर इस उत्तर ने इसे आपके लिए हल किया है, तो कृपया इसे उत्तर के रूप में चिह्नित करें। धन्यवाद। –

7

फोन वाकई Advanced खंड में Generate Task based operations चयनित है। इस वापस लौटने Task<string>

+0

यह एक ईवेंट आधारित मॉडल का उपयोग कर रहा है, इसलिए 'FromAsync' मदद नहीं करेगा। – Servy

+0

@ सर्वी मैंने प्रश्न में यूआरएल का इस्तेमाल किया और स्वचालित रूप से प्रतीक्षा करने योग्य कार्यों को उत्पन्न किया। कुछ ग़लत है? – I4V

+0

मैं कार्य आधारित परिचालन जेनरेट करने में असमर्थ हूं क्योंकि यह गहरा हुआ है। ऐसा लगता है कि यह WP8 परियोजनाओं के लिए अक्षम है। [यह विषय] देखें (http://stackoverflow.com/questions/13266079/wp8-sdk-import-service-reference-with-task-based-operations-not-possible) – MPeli

-3

आप तो विधियों का इंतजार करने में सक्षम होना चाहते हैं, उन्हें कार्य वापस करना चाहिए। आप एक विधि का इंतजार नहीं कर सकते जो शून्य लौटाता है। यदि आप उन्हें एक मूल्य वापस करना चाहते हैं, जैसे int उन्हें Task<int> वापस करना चाहिए तो विधि int वापस आनी चाहिए।

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

तो फिर तुम फोन कर सकते हैं

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

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

वह पूछ रहा है * कैसे * ऐसा करने के लिए। वह नहीं जानता कि एक घटना आधारित मॉडल कैसे कार्य कर सकता है। – Servy

+0

वही विधि लें जो आपने किया था और केवल शून्य के बजाय कार्य डालें। फिर आप अपने कार्यक्रम में प्रतीक्षा कर सकते हैं। कोई अतिरिक्त काम की जरूरत नहीं है। इसका उपयोग कैसे करें एक अलग कहानी है। –

+0

आप विधि के रिटर्न प्रकार को बदल नहीं सकते हैं और किया जा सकता है। प्रश्न विशेष रूप से यह पूछ रहा है कि विधि के कार्यान्वयन को कैसे बदला जाए ताकि यह वास्तव में एक कार्य वापस कर सके। सवाल यह पूछ रहा है कि डेटा तैयार होने पर पूरा होने वाला 'कार्य' कैसे बनाया जाता है, और आप इसका उत्तर नहीं देते हैं। यहूदा के जवाब को देखें कि आप इसे वास्तव में कैसे कार्यान्वित करते हैं। – Servy

3

मैं पिछले वर्ष की तुलना कई बार यह एक जोड़े को ऐसा करने के लिए किया है और मैं दोनों का उपयोग किया है @ ऊपर यहूदा का कोड और original example वह संदर्भित किया है, लेकिन प्रत्येक बार जब मैंने दोनों के साथ निम्न समस्या पर हिट किया है: async कॉल काम करता है लेकिन पूरा नहीं करता है। अगर मैं इसके माध्यम से कदम उठाता हूं तो मैं देख सकता हूं कि यह TransferCompletion विधि दर्ज करेगा लेकिन e.UserState == tcs हमेशा false होगा।

यह पता चला है कि ओपी के loginAsync जैसी वेब सेवा एसिंक विधियों में दो हस्ताक्षर हैं।दूसरा userState पैरामीटर स्वीकार करता है। समाधान इस पैरामीटर के रूप में बनाए गए TaskCompletionSource<T> ऑब्जेक्ट को पास करना है। इस तरह e.UserState == tcs सच हो जाएगा।

ओपी में, e.UserState == tcs को कोड काम करने के लिए हटा दिया गया था जो समझ में आता है - मुझे भी लुभाना था। लेकिन मुझे विश्वास है कि सही घटना पूरी होने के लिए यह है।

पूरा कोड है:

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; 
} 

वैकल्पिक रूप से, मेरा मानना ​​है कि एक tcs.Task.AsyncState संपत्ति भी है कि userState प्रदान करेगा है।

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(); 
} 

यह है कि मैं क्या शुरू में करने की कोशिश की के रूप में यह एक लाइटर दृष्टिकोण लग रहा था और मैं पूरी TaskCompletionSource वस्तु के बजाय एक Guid गुजारें सकता था: तो आप की तरह कुछ कर सकता है। यदि आप रुचि रखते हैं तो स्टीफन क्लेरी में good write-up of the AsyncState है।

संबंधित मुद्दे

 संबंधित मुद्दे