2016-09-15 2 views
5

私はAsp.Net Core Identityを使用しており、ユーザーリストとそのロールをViewModelに投影するコードを単純化しようとしています。このコードは機能しますが、それを単純化しようとすると、私はエラーと好奇心の狂った渦巻きになってしまいました。ここでasync/await insideを使用してください。ラムダを選択してください

が私の作業コードです:コードを単純化しようとして

 var allUsers = _userManager.Users.OrderBy(x => x.FirstName); 
     var usersViewModel = new List<UsersViewModel>(); 

     foreach (var user in allUsers) 
     { 
      var tempVm = new UsersViewModel() 
      { 
       Id = user.Id, 
       UserName = user.UserName, 
       FirstName = user.FirstName, 
       LastName = user.LastName, 
       DisplayName = user.DisplayName, 
       Email = user.Email, 
       Enabled = user.Enabled, 
       Roles = String.Join(", ", await _userManager.GetRolesAsync(user)) 
      }; 

      usersViewModel.Add(tempVm); 
     } 

、私はこの (壊れコード)ような何かを行うことができます考え出し:

 var usersViewModel = allUsers.Select(user => new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
     }).ToList(); 

この私はユーザの前にラムダ式でasyncキーワードを使用していないので、ブレークします。私はユーザー前に非同期を追加行うときしかし、私は

私の推測では、GetRolesAsync()メソッドは、タスクとを返しているということである「非同期ラムダ式は、式ツリーに変換することができません」と言うまた別のエラーを取得しますそのタスクの実際の結果ではなく、ロールに割り当てます。私が私の人生を理解できないように見えるのは、それを働かせる方法です。

私は、運が悪い過去の日に多くの方法を研究し、試しました。

Is it possible to call an awaitable method in a non async method?

https://blogs.msdn.microsoft.com/pfxteam/2012/04/12/asyncawait-faq/

Calling async method in IEnumerable.Select

How to await a list of tasks asynchronously using LINQ?

how to user async/await inside a lambda

How to use async within a lambda which returns a collection

:ここで私が見たいくつかはあります

確かに、私は非同期/待機中の仕事を完全に理解していないので、それはおそらく問題の一部です。私のforeachのコードは動作しますが、私はそれが私のやり方で動作するようにする方法を理解できるようにしたいと思います。私はすでにそれに多くの時間を費やしてきたので、これは良い最初の質問になると思いました。

ありがとうございます!

編集

私は私が私が重複した質問としてフラグ付けされないように、このためには研究論文のそれぞれの場合に何をしたか説明する必要があると思います - と私はそれを避けるために本当に懸命に試みました: - /。問題は同じように聞こえるが、結果は一致しない。これらの両方のエラーメッセージが生成

var usersViewModel = allUsers.AsEnumerable().Select(async user => new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
     }).ToList(); 

を「私もそうのようなAsEnumerableを使用してみました

public async Task<ActionResult> Users() 
    { 
     var allUsers = _userManager.Users.OrderBy(x => x.FirstName); 
     var tasks = allUsers.Select(GetUserViewModelAsync).ToList(); 
     return View(await Task.WhenAll(tasks)); 
    } 

    public async Task<UsersViewModel> GetUserViewModelAsync(ApplicationUser user) 
    { 
     return new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = String.Join(", ", await _userManager.GetRolesAsync(user)) 
     }; 
    } 

:答えとしてマークされた記事の場合は、私は次のコードを試してみましたInvalidOperationException:前の操作が完了する前に、このコンテキストで2番目の操作が開始されました。どのインスタンスメンバーもスレッドセーフであることは保証されていません。」

この時点では、私のオリジナルのForEachが最良の賭けになるかもしれないようですが、私はまだこれを行う正しい方法が何であるか疑問に思っています。非同期メソッドを使用してそれを実行していた

編集2 - 回答で ツェンのコメントのおかげで(および他のいくつかの研究)私は次のコードを使用して、物事を動作させることができました:。

 var userViewModels = allUsers.Result.Select(async user => new UsersViewModel 
     { 
      Id = user.Id, 
      UserName = user.UserName, 
      FirstName = user.FirstName, 
      LastName = user.LastName, 
      DisplayName = user.DisplayName, 
      Email = user.Email, 
      Enabled = user.Enabled, 
      Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
     }); 
     var vms = await Task.WhenAll(userViewModels); 
     return View(vms.ToList()); 

を今私は誰もが私はSQL Profilerを見て、DBが実際にどれくらいのヒット数を得ているかを調べ始めました。Matt Johnsonが述べたように、N + 1が多いです。

これは私の質問に答えるが、今質問を実行する方法を再考しているだけで、メインビューにその役割をドロップして、各ユーザーが選択されるたびにそれらをプルすることができます。私は間違いなくこの質問を通してたくさんのことを学びました(そして私が知らないものをもっと学んだ)ので、みんなに感謝します。

+0

'私の推測では、GetRolesAsync()メソッドはTaskを返して、そのタスクの実際の結果ではなくRolesに割り当てていると思います。おそらくそうではないでしょう。なぜなら、 'await'がタスクの結果を得るからです。 – mason

+2

'Select'の前に' AsEnumerable'を置くようにしてください。それは、それをEFのための式ツリーに変換するのではなく、オブジェクトにLInqで実行されます。 – juharr

+0

VaRのusersViewModels =は(Task.WhenAll(allUsers.AsEnumerable()を待つ。(非同期ユーザーを選択=>新しいUsersViewModel {ID = user.Id、 ユーザー名= user.UserName、 姓= user.FirstName、 氏名= user.LastName、 のDisplayName = user.DisplayName、 メール= user.Email、 有効= user.Enabled、 役割= string.Join( ""、_userManager.GetRolesAsync(ユーザ)を待つ) })))。 ToList(); –

答えて

7

ここでは2つのものを混ぜていると思います。式ツリーと代理人。 Lambdaは両方を表現するのに使うことができますが、メソッドが受け入れるパラメータの型に依存します。

ラムダはAction<T>又はFunc<T, TResult>としてデリゲート(基本的に匿名関数/メソッド)に変換されるメソッドに渡されます。

Expression<T>を受け入れるメソッドにラムダ式を渡すと、ラムダから式ツリーが作成されます。式ツリーは、コードを記述するコードですが、コードそのものではありません。

つまり、表現木は実行可能コードに変換されているため実行できません。実行時に式ツリーをコンパイルし、デリゲートのように実行することができます。

ORMフレームワークでは、式ツリーを使用して、異なるコード(たとえばデータベースクエリなど)に変換できる「コード」を記述したり、実行時にコードを動的に生成したりできます。

Expression<T>を受け入れるメソッドでは、asyncを使用できません。 AsEnumerable()に変換するときに機能する理由は、IEnumerable<T>を返し、LINQメソッドがFunc<T, TResult>を受け入れるためです。しかし、それは基本的にクエリ全体をフェッチし、メモリ内のすべてのものを処理するので、投影を使用できません(または式と投影だけを使用する前にデータをフェッチしてください)。

あなたはこのような何かを試みることができる:

// Filter, sort, project it into the view model type and get the data as a list 
var users = await allUsers.OrderBy(user => user.FirstName) 
          .Select(user => new UsersViewModel 
    { 
     Id = user.Id, 
     UserName = user.UserName, 
     FirstName = user.FirstName, 
     LastName = user.LastName, 
     DisplayName = user.DisplayName, 
     Email = user.Email, 
     Enabled = user.Enabled 
    }).ToListAsync(); 

// now that we have the data, we iterate though it and 
// fetch the roles 
var userViewModels = users.Select(async user => 
{ 
    user.Roles = string.Join(", ", await _userManager.GetRolesAsync(user)) 
}); 

最初の部分は、データベース上で完全に行われますが、あなたはすべてのあなたの利点を保つ(順序がデータベースに起こるので、あなたは何をする必要はありません。つまり、結果と制限呼び出しを取り出した後のメモリ内ソートは、DBなどからフェッチされたデータを制限します)。

2番目の部分は、メモリ内の結果を反復処理し、各一時的なモデルのデータを取り出し、最終的にビューモデルにマップします。

関連する問題