2017-02-16 8 views
8

サービスコアとして.NETコアWeb APIを使用しています。サービス層にはすべてのEFコードがあります。コントローラのアクションでは、このコードwebapiでasync awaitを使用するためのベストプラクティス

protected Task<IActionResult> NewTask(Func<IActionResult> callback) 
{ 
    return Task.Factory.StartNew(() => 
    { 
     try 
     { 
      return callback(); 
     } 
     catch (Exception ex) 
     { 
      Logger.LogError(ex.ToString()); 
      throw; 
     } 
    }); 
} 

とbasecontrollerがある場合

Iは、例えば、上記の方法では、サービスへのすべての呼び出しをラップ:

[HttpGet("something")] 
public async Task<IActionResult> GetSomething(int somethingId) 
{ 
    return await NewTask(() => 
    { 
     var result = _somethingService.GetSomething(somethingId); 

     if (result != null) 
      return Ok(result); 
     else 
      return NotFound("Role not found"); 
    }); 
} 

は、私はアクションで複数のサービスコールを持っているか、他のWebサービスを呼び出すことも明日考えると、この正しいパターンです。お知らせ下さい。

+4

をあなたはここで達成しようとしているのかについての詳細な情報をお願いできますか?あなたのコードは基本的に、バックグラウンドスレッドで同期作業を行うことで非同期作業を偽装します。 Task.Factory.StartNewを使用することはかなり危険であり、代わりにTask.Runを使用する必要がありますが、ここでもTask.Runは必要ありません。 –

+0

何らかのサービスには、entityframeworkコアを使用するいくつかの粗末な操作があります。またtommorrow私は、httpClientを使用して外部Webからのフィードを取得する呼び出しを持つ、feedServiceというサービスを持つかもしれません。私は、私のAPIを非同期的なものから恩恵を受けることを望みます。上記のパターンは、これらのニーズ+あらゆる懸念や改善に役立ちます。 – krishna

答えて

17

私は私のAPIは非同期の恩恵を受けるしたいパターン上記thing.doesいいえ、そうでないこれらのニーズ

にサービスを提供します待っています。スレッドプール上で同期作業を実行すると、同期非同期コードの欠点がありますが、どちらの利点もありません。

何かサービスが

現在entityframeworkコアを使用して、いくつかのCRUD操作を持っている、あなたのアクションメソッドは、私が「非同期偽物」と呼ぶものである - それは非同期になります(例えば、await使用)が、実際にはありますバックグラウンドスレッドでブロッキングコードを実行するだけです。 ASP.NETでは、真の非同期が必要です。これは、すべて非同期でなければならないことを意味します。 ASP.NETでこれがなぜ悪いのかについては、intro to async on ASP.NET articleの前半部分を参照してください(ほとんど非同期ASP.NETを扱いますが、非同期要求と非同期要求の最初の部分はどのような種類のサーバーでも有効です)。

これを真に非同期にするには、最低レベル(この場合はEFCoreコール)から開始する必要があります。それらはすべて非同期をサポートします。したがって、x.FirstOrDefault()のようなAPI呼び出しをawait x.FirstOrDefaultAsync()に置き換えてください(そして、あなたの作成/更新/削除のすべてに対して同じです)。

次に、async/awaitをそこから自然に生育させる。コンパイラがあなたを案内します。あなたは、このようなとして消費することができ、あなたのsomethingService上の非同期メソッドになってしまいます:

[HttpGet("something")] 
public async Task<IActionResult> GetSomething(int somethingId) 
{ 
    var result = await _somethingService.GetSomethingAsync(somethingId); 
    if (result != null) 
    return Ok(result); 
    else 
    return NotFound("Role not found"); 
} 
+0

コア非同期呼び出しが同期呼び出しより遅い場合は、アドバイスをお願いします。 – krishna

+0

@ user1603828:その差はごくわずかです。それがあなたが見ているものでない場合は、EFチームに連絡することをお勧めします。 –

+0

あなたのブログには面白い情報がたくさんあります。 – krishna

6

さて、まず第一に、あなたはTask.Factory.StartNewの使用を停止する必要があり、あなたがそのあなたの重いCPUバウンドの仕事を持っている場合にのみ、Task.Runを使用しますスレッドプールスレッド上で実行したいあなたの場合、あなたは本当にそれを必要としません。また、メソッドを呼び出すときにはTask.Runを使用し、メソッドの実装では使用しないでください。その詳細についてはhereを読むことができます。

実際にあなたが望むのは、実際にデータベースに電話をかけて、使用したいときにサービス内に非同期の作業をすることです。 async/awaitだけでなく、バ​​ックグラウンドスレッドでいくつかのものを実行します。

(あなたがサービスを必要と確信している場合は)基本的に、あなたのサービスは次のようになります。

class PeopleService 
{ 
    public async Task<Person> GetPersonByIdAsync(int id) 
    { 
     Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id); 
     return randomPerson; 
    } 
} 

あなたはあなたのサービスは現在、データベースへの非同期呼び出しを行い、それが基本的にどのようなあなたのパターンがすべきだ見ることができるようにさあ。

サービスを非同期にした後で、アクションのデータを簡単に使用できるようにする必要があります。

あなたの行動は次のようになります。

[HttpGet("something")] 
public async Task<IActionResult> GetPerson(int id) 
{ 
    var result = await PeopleService.GetPersonByIdAsync(id); 

    if (result != null) 
     return Ok(result); 
    else 
     return NotFound("Role not found"); 
} 
+0

私はefコアの非同期メソッドについて少し研究しましたが、それは同期efメソッドよりも遅いと言いました。アドバイスしてください。 – krishna

+0

@kirshna非同期メソッドは遅くなります。彼らはそれらを同期して実行する際に避けられないオーバーヘッドを持っています。しかし、スループットを向上させることができます。したがって、スループットとパフォーマンスのバランスは考慮する必要があります。パフォーマンスに関する質問には常に当てはまるとおり、複雑です。 https://www.dotnetrocks.com/?show=1433それを聞いて、私が何を言っているのか見てみましょう。 – thinklarge

関連する問題