2016-05-24 16 views
2

私はTAPとasync/newを実際にC#に入れていますので、ここで悪いコードの匂いがするかもしれません。 :-)MVCコントローラからの非同期呼び出しを含むサービスを呼び出すための正しいパターン

私は次のように見えるサービスメソッドがあります。

public OzCpAddOrUpdateEmailAddressToListOutput AddOrUpdateEmailAddressToList(
    OzCpAddOrUpdateEmailAddressToListInput aParams) 
{ 
    var result = new OzCpAddOrUpdateEmailAddressToListOutput(); 
    try 
    { 
     var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey); 
     Task<Member> mailChimpResult = 
      mailChimManager.Members.AddOrUpdateAsync(
       aParams.Listid, 
       new Member                          
       { 
        EmailAddress = aParams.EmailAddress 
       }); 

     //Poll async task until it completes. 
     //Give it at most 8 seconds to do what it needs to do 
     var outOfTime = DateTime.Now.AddSeconds(8); 
     while (!mailChimpResult.IsCompleted) 
     { 
      if (DateTime.Now > outOfTime) 
      { 
       throw new Exception("Timed out waiting for MailChimp API."); 
      } 
     } 

     //Should there have been a problem with the call then we raise an exception 
     if (mailChimpResult.IsFaulted) 
     { 
      throw new Exception(
       mailChimpResult.Exception?.Message ?? 
       "Unknown mail chimp library error.", 
       mailChimpResult.Exception); 
     } 
     else 
     { 
      //Call to api returned without failing but unless we have 
      //the email address subscribed we have an issue 
      if (mailChimpResult.Result.Status != Status.Subscribed) 
      { 
       throw new Exception(
        $"There was a problem subscribing the email address 
        {aParams.EmailAddress} to the mailchimp list id 
        {aParams.Listid}"); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message); 
    } 
    return result; 
} 

をしかし、私はMVCコントローラアクションmailChimpResult.IsCompletedから呼び出すときは常にfalseを返し、最終的に私はタイムアウトを打ちます。

これは、HttpClient IsComplete always return falseのように非同期呼び出しを連鎖していないためであり、異なるスレッドのためにこの動作が「期待」されているためです。

私は私のサービスメソッドは、それが何をしているかのasync自然の複雑さを隠し、単につまり、私のアクションメソッドでは同期呼び出しに見えるものをやりたいしかし:

var mailChimpResult = 
    _PlatformMailChimpService.AddOrUpdateEmailAddressToList(
     new OzCpAddOrUpdateEmailAddressToListInput                                    
     { 
      EmailAddress = aFormCollection["aEmailAddress"],                                   
      Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,                                   
      MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value 
     }); 

if (mailChimpResult.Result == true) 
{ 
    //So something 
} 
+1

「async」を実行する必要があります。 「隠蔽」すると常に痛い発見につながります。通常はデッドロックです(http://stackoverflow.com/questions/13140523/await-vs-task-wait-deadlock)。本質的にその周りには道がありません。 –

+0

@AlexeiLevenkovしかし、私のサービスコールは非同期ハンドルなどを返さないので、どのように実装すればよいでしょうか。あなたは私のコードに基づいて私に例を与えることができますか? – TheEdge

+1

サービスコール中に隠しています。それをポーリングするのではなく、 'AddOrUpdateAsync'の呼び出しを待つ必要があります。これは大きなコードの匂いです。次に、サービスメソッドを非同期にして、コントローラ内で待っています。 – Rhumborl

答えて

1

理想的にはあなたが避けなければなりませんコードとTask<T>オブジェクトの.Result.IsFaultedのプロパティ、つまりコードの1番のコードです。これらのオブジェクトを使用する場合は、スタック全体にasyncawaitを使用する必要があります。

public async Task<OzCpAddOrUpdateEmailAddressToListOutput> 
    AddOrUpdateEmailAddressToList(
     OzCpAddOrUpdateEmailAddressToListInput aParams) 
{ 
    var result = new OzCpAddOrUpdateEmailAddressToListOutput(); 
    try 
    { 
     var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey); 
     Member mailChimpResult = 
      await mailChimManager.Members.AddOrUpdateAsync(
       aParams.Listid, 
       new Member                          
       { 
        EmailAddress = aParams.EmailAddress 
       }); 
    } 
    catch (Exception ex) 
    { 
     result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message); 
    } 
    return result; 
} 

不要なポーリングとプロパティの検査をすべて削除できたことに注目してください。このメソッドをTask<OzCpAddOrUpdateEmailAddressToListOutput>として返し、asyncというキーワードで飾ります。これによりメソッド本体にawaitキーワードを使用することができます。 await.AddOrUpdateAsyncMemberが得られます。それが「非同期」という言葉を後置することがベストプラクティスと考えられている

var mailChimpResult = 
    await _PlatformMailChimpService.AddOrUpdateEmailAddressToList(
     new OzCpAddOrUpdateEmailAddressToListInput                                    
     { 
      EmailAddress = aFormCollection["aEmailAddress"],                                   
      Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,                                   
      MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value 
     }); 

if (mailChimpResult.Result == true) 
{ 
    //So something 
} 

サービスにかかるコールは同じasyncのパラダイムとTaskまたはTask<T>戻り値の型を持つawaitキーワード以下、似ていますそれが非同期であることを示すために、すなわち、 AddOrUpdateEmailAddressToListAsync

+0

そしてコントローラメソッドは 'var mailChimpResult = await _PlatformMailChimpService.AddOrUpdateEmailAddressToList(...);になります。 if(mailChimpResult.Result == true){} 'のように、まだ複雑さを隠しています。一つの単語を追加する以外は、 – Rhumborl

+0

@Davidはそれに感謝します。私はあなたの返事を見る前にリファクタリングしました。さらにコントローラの動作を非同期にすることだけが追加されました。 – TheEdge

関連する問題