2011-01-12 4 views
0

3つのテーブル、ユーザー、製品、購入があるとします。 ユーザーが行った購入を表示する必要があるビューがあります。ASP.NET MVCとEF4 Entity Framework - エンティティの使用と、必要なフィールドのみの取得とのパフォーマンス上の問題はありますか?

私が行って、必要なデータを検索できます。

from p in DBSet<Purchases>.Include("User").Include("Product") select p; 

は、しかし、私はそれが完全なオブジェクトを取得しますので、これはパフォーマンスへの影響を有し得ることが懸念しています。 代わりに、私は私が必要なフィールドだけを選択することができます:

from p in DBSet<Purchases>.Include("User").Include("Product") select new SimplePurchaseInfo() { UserName = p.User.name, Userid = p.User.Id, ProductName = p.Product.Name ... etc }; 

だから私の質問は次のとおりです。これを行う中 いただきましベストプラクティス?

== EDIT

すべての返信いただきありがとうございます。

[質問1]:私はすべてのビューは、そのビューのために非常に具体的なデータとフラットのviewmodelsで動作するかどうかを知りたい、またはのviewmodelsは、エンティティオブジェクトが含まれている必要があります。

レアル例:ユーザーが製品

var query = from dr in productRepository.FindAllReviews() 
      where dr.User.UserId = 'userid' 
      select dr; 
string sql = ((ObjectQuery)query).ToTraceString(); 

SELECT [Extent1].[ProductId] AS [ProductId], 
     [Extent1].[Comment] AS [Comment], 
     [Extent1].[CreatedTime] AS [CreatedTime], 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Rating] AS [Rating], 
     [Extent1].[UserId] AS [UserId], 
     [Extent3].[CreatedTime] AS [CreatedTime1], 
     [Extent3].[CreatorId] AS [CreatorId], 
     [Extent3].[Description] AS [Description], 
     [Extent3].[Id] AS [Id1], 
     [Extent3].[Name] AS [Name], 
     [Extent3].[Price] AS [Price], 
     [Extent3].[Rating] AS [Rating1], 
     [Extent3].[ShopId] AS [ShopId], 
     [Extent3].[Thumbnail] AS [Thumbnail], 
     [Extent3].[Creator_UserId] AS [Creator_UserId], 
     [Extent4].[Comment] AS [Comment1], 
     [Extent4].[DateCreated] AS [DateCreated], 
     [Extent4].[DateLastActivity] AS [DateLastActivity], 
     [Extent4].[DateLastLogin] AS [DateLastLogin], 
     [Extent4].[DateLastPasswordChange] AS [DateLastPasswordChange], 
     [Extent4].[Email] AS [Email], 
     [Extent4].[Enabled] AS [Enabled], 
     [Extent4].[PasswordHash] AS [PasswordHash], 
     [Extent4].[PasswordSalt] AS [PasswordSalt], 
     [Extent4].[ScreenName] AS [ScreenName], 
     [Extent4].[Thumbnail] AS [Thumbnail1], 
     [Extent4].[UserId] AS [UserId1], 
     [Extent4].[UserName] AS [UserName] 
     FROM [ProductReviews] AS [Extent1] 
     INNER JOIN [Users] AS [Extent2] ON [Extent1].[UserId] = [Extent2].[UserId] 
     LEFT OUTER JOIN [Products] AS [Extent3] ON [Extent1].[ProductId] = [Extent3].[Id] 
     LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent1].[UserId] = [Extent4].[UserId] 
     WHERE N'615005822' = [Extent2].[UserId] 

または

from d in productRepository.FindAllProducts() 
from dr in d.ProductReviews 
where dr.User.UserId == 'userid' 
orderby dr.CreatedTime 
select new ProductReviewInfo() 
     { 
      product = new SimpleProductInfo() { Id = d.Id, Name = d.Name, Thumbnail = d.Thumbnail, Rating = d.Rating }, 
      Rating = dr.Rating, 
      Comment = dr.Comment, 
      UserId = dr.UserId, 
      UserScreenName = dr.User.ScreenName, 
      UserThumbnail = dr.User.Thumbnail, 
      CreateTime = dr.CreatedTime 
     }; 

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
[Extent1].[Thumbnail] AS [Thumbnail], 
[Extent1].[Rating] AS [Rating], 
[Extent2].[Rating] AS [Rating1], 
[Extent2].[Comment] AS [Comment], 
[Extent2].[UserId] AS [UserId], 
[Extent4].[ScreenName] AS [ScreenName], 
[Extent4].[Thumbnail] AS [Thumbnail1], 
[Extent2].[CreatedTime] AS [CreatedTime] 
FROM [Products] AS [Extent1] 
INNER JOIN [ProductReviews] AS [Extent2] ON [Extent1].[Id] = [Extent2].[ProductId] 
INNER JOIN [Users] AS [Extent3] ON [Extent2].[UserId] = [Extent3].[UserId] 
LEFT OUTER JOIN [Users] AS [Extent4] ON [Extent2].[UserId] = [Extent4].[UserId] 
WHERE N'userid' = [Extent3].[UserId] 
ORDER BY [Extent2].[CreatedTime] ASC 

[質問2]レビュー:いただきまし醜い外部結合と?

+1

NB:2番目のクエリで 'Include'呼び出しは不要です。あなたが予測しているので、EFは必要なものを見つけ出します。 –

答えて

-1

両方ともほぼ同じクエリを作成します。つまり、2つの内部結合を使用して1つのテーブルから選択します。データベースの観点から変わるのは、返されるフィールドの量だけですが、それほど重要ではありません。

は、私が(それも存在する場合)ここではパフォーマンスヒットからDRY勝利だと思う:ので、私の呼び出しは、最初のオプションのために行くです。これを行うには、一緒にバッチ物事の束をすることができますので、もし一般的に

+3

私は同意しがちです。私はクエリが(SELECT *または類似のもの)のように実装されていることを見てきましたし、低帯域幅の接続では、特にアイテムが返された場合(たとえばフィルタリングされた結果が1つだけではなく、アイテム数が100または1000を超える可能性があります)。 –

+0

彼はMVCを使用していると指定しているので、SQLサーバは同じマシン上、または同じ場所にあるマシン上に座っているので、帯域幅は問題ではありません。 – Gidon

1

、のみそうしないと、ネットワークトラフィックを支払う、あなたが必要なものを取得するが、あなたのアプリケーションがあまりにもおしゃべりではないように、十分な情報を取得するために念頭に置いておきますあなたがデータベースに戻っていくつかのものを取り戻す必要があるたびにコストがかかります。 (それはあなたが本当に必要なものの場合)この場合

は、あなただけのこれらの情報が必要になると仮定すると、私は第二のアプローチとなるだろう。

+0

彼がクエリから取得したものは、すぐにクライアントに送信されず、MVCによって消費され、HTML(またはjson/xml)に変換されます。したがって、クライアント/サーバーの帯域幅はこの問題では問題になりません。 – Gidon

+0

それは私が意味するものではありません。私が行っているプロジェクトのほとんどでは、SQLボックスはWebボックスとは異なります。したがって、内部的なものであっても、ボックス間でネットワークトラフィックが発生します。 –

1

私はあなたのLINQのツーエンティティにマップされていない.NET型になるマップすることはできませんので、2番目のクエリは、例外をスローすると思います。匿名の型を返し、Linqからオブジェクトへのオブジェクトにマップするか、投影のためのいくつかの高度な概念、つまりQueryView(ESQLの投影)またはDefiningQuery(新しい読み取り専用エンティティにマップされたカスタムSQLクエリ)を使用する必要があります。

一般的に、あなたのエンティティのデザインについてです。単一の小さなエンティティを選択した場合、投影の代わりにすべてを読み込む大きな違いはありません。エンティティのリストを選択している場合は、結果に必要でないnvarchar(max)やvarbinar(max)のようなカラムがテーブルに含まれている場合、投影を考慮する必要があります。あなたはフィルタリング(またはそのことについては発注)したいとき.Include

+2

"Linqからエンティティへのマップされていない.NETタイプに結果をマップすることはできません"これは完全に間違っています。 EFのどのバージョンでもこれは決して真実ではありません。 POCOに投影するとうまくいきます。あなたが投影することができない唯一のものはエンティティタイプです。 –

+1

はい私はそれをテストしただけで動作します。修正をありがとう。 –

+0

@Craig Stuntz - 本当ですか?過去に私は 'IQueryable'(' ObjectQuery ')をDTOクラスに投影しようとしましたが、"ストア式に変換できません "というエラーが出ました。私は匿名型に投影するか、* first *( '.ToList')* then * projectというクエリを実現しなければなりませんでした。私は、パラメータなしのctorまたは匿名のタイプのPOCOしか投影できないという印象を受けています。私はその質問では、OPはDTOまたはViewModelに投影しようとしているか、いずれも上記の要件を満たしていないため、エラーになると考えています。 – RPM1984

1

イーガーローディングは本当に素晴らしいプレーしません。

最初のクエリは、この基本的であること:

select p.*, u.*, p2.* 
from products p 
left outer join users u on p.userid = u.userid 
left outer join purchases p2 on p.productid = p2.productid 
where u.userid == @p1 

が本当に何をしたいということですか?

ユーザーが行った購入を表示する必要があるビューがあります。

なぜ「製品」が含まれていますか?

はそれだけではないはずです。

from p in DBSet<Purchases>.Include("User") select p; 

あなたの2番目のクエリはエラーになります。 モデルのエンティティにプロジェクトする必要があります。または、匿名型で、ランダムなクラス/ DTOではありません。 、最も簡単でも、あなたの現在のシナリオでオプションを実行正直に言うと

がFK自体に照会することです:

生成する必要があります
var purchasesForUser = DBSet<Purchases>.Where(x => x.UserId == userId); 

select p.* 
from products p 
where p.UserId == @p1 

上記のクエリもちろん、モデルに外部キーを含める必要があります。

モデルにFKがない場合は、匿名型の投影の形で多くのLINQ-Entitiesをトリッキーにする必要があります。

全体的に、は最適化するために外に出ない。シナリオ/ビジネス要件に合わせたクエリを作成し、必要に応じて最適化するか、ストアドプロシージャ、ビュー、コンパイル済みクエリなどのLINQ-Entitiesの代替案を探します。

注意:早すぎる最適化はすべての悪の根源です。

* EDIT - 更新を質問に対して*

[質問1]:私はすべてのビューは、そのビューのために非常に特定のデータをフラットのviewmodelsで動作するかどうかを知りたい、または必要があるのviewmodelsエンティティオブジェクトを含みます。

はい - ViewModelには、そのビューに必要なものだけを含める必要があります。それ以外の場合、ViewModelはなぜですか? EFモデルに直接バインドすることもできます。したがって、ビューに必要なフィールドだけを表示するViewModelを設定します。

[質問2]:醜い外部結合は何ですか?

これは.Includeのデフォルトの動作です。 .Include常には、左外部結合を生成します。

+0

この多対多の関係で、私はユーザーと製品からの情報を表示する必要があります。例:ユーザー名、製品名 – Ant

+0

@Ant-あなたのモデルには関係が何であるかは不明です。 EDMXファイルをスクリーンショットして質問に入れることができますか? – RPM1984

+0

ユーザーが商品を確認できます。製品は多くのユーザーが見直すことができます。 申し訳ありませんが、元の投稿を編集しました。 – Ant

関連する問題