EF 5 でクエリを作成するための「ベスト プラクティス」がどのようなものであるかに興味があります。
たとえば、多対多の関係を持つ Movie と Genre の 2 つのテーブルがあるとします。そのため、FK として MovieId と GenreId を持つ 3 つ目のテーブル GenreMovie があります。
1 つのジャンルに複数の映画があることを意味しますが、1 つの映画に複数のジャンルが混在することもあります。
クライアント側には、左側にジャンル リスト、右側に映画リストの 2 つのグリッドがあります。
選択したジャンルの映画リストには、そのジャンルの映画だけでなく、このジャンルに属する映画がチェックされるすべての映画が表示されます。
この選択は、同じ結果をもたらす 2 つの方法で実行できます。
int ジャンル ID = 1; (id=1のジャンルを選択)
EF アソシエーションの使用
var q = db.Movies .Select(m => new { MovieId = m.MovieId, Title = m.Title, Selected = m.GenreMovies.Where(gm => gm.GenreId == genreId).Count() > 0, });
Linq 結合の使用
var q = from m in db.Movies join gm in db.GenreMovies.Where(gm => gm.GenreId == genreId) on m.MovieId equals gm.MovieId into gms from gm in gms.DefaultIfEmpty() select new { MovieId = m.MovieId, Title = m.Title, Selected = gm != null };
明らかに、最初の方法はコードが少なく、よりシンプルに見えます。
しかし、それは本当に良い方法であり、ベストプラクティスなのでしょうか?
最適化に関して、EF アソシエーションが常に SQL クエリに最適な変換を行うとは限らないと聞いたので。
また、Sql Profiler を調べて、両方の方法で結果のクエリを確認しました。そして、私は次のことを見ました:(1.より多くの行でクエリを作成するようですが、効率が悪いかどうかはわかりません)???
数十万のレコードでテストを試みましたが、どちらの方法も速すぎて違いがわかりませんでした。
1.
exec sp_executesql N'SELECT
[Project2].[MovieId] AS [MovieId],
[Project2].[Title] AS [Title],
CASE WHEN ([Project2].[C1] > 0) THEN cast(1 as bit) WHEN ( NOT ([Project2].[C2] > 0)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT
[Project1].[MovieId] AS [MovieId],
[Project1].[Title] AS [Title],
[Project1].[C1] AS [C1],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[GenreMovie] AS [Extent3]
WHERE ([Project1].[MovieId] = [Extent3].[MovieId]) AND ([Extent3].[GenreId] = @p__linq__0)) AS [C2]
FROM ( SELECT TOP (1000)
[c].[MovieId] AS [MovieId],
[c].[Title] AS [Title],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[GenreMovie] AS [Extent2]
WHERE ([c].[MovieId] = [Extent2].[MovieId]) AND ([Extent2].[GenreId] = @p__linq__0)) AS [C1]
FROM [dbo].[Movie] AS [c]
) AS [Project1])
AS [Project2]',N'@p__linq__0 int',@p__linq__0=1
2.
exec sp_executesql N'SELECT
[Limit1].[MovieId] AS [MovieId],
[Limit1].[Title] AS [Title],
CASE WHEN ([Limit1].[GenreMovieId] IS NOT NULL) THEN cast(1 as bit) WHEN ([Limit1].[GenreMovieId] IS NULL) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT TOP (1000)
[Extent1].[MovieId] AS [MovieId],
[Extent1].[Title] AS [Title],
[Extent2].[GenreMovieId] AS [GenreMovieId]
FROM [dbo].[Movie] AS [Extent1]
LEFT OUTER JOIN [dbo].[GenreMovie] AS [Extent2] ON ([Extent2].[GenreId] = @p__linq__0) AND ([Extent1].[MovieId] = [Extent2].[MovieId]))
AS [Limit1]',N'@p__linq__0 int',@p__linq__0=1