1

多対多のリンク テーブルと共に SQL Server に and テーブルがありProducts、この構造に対して生成された Entity Framework (VS2012、.NET4.5) モデルがあります。ImagesProductImages

GetImageList製品に関連付けられた画像を一覧表示するメソッドを持つ WCF サービスがあります。速度を上げるために、テーブルからいくつかの列のみを返したいと思います。特に、高解像度の画像を格納するために非常に大きくなる可能性のあるおよび列をImages除外します。ImageBinaryImageThumbnailBinary

クエリの要点を証明するために、ファイル名がfred.jpg.

まずProduct、ナビゲーション プロパティを取得して使用することから始めました。

Product p = ctx.Products.SingleOrDefault(x => x.Code == productCode);

if (p != null)
{
    var images = p.Images.Where(x => (x.FileName == "fred.jpg") && (!imageTypeId.HasValue || x.ImageTypeId == imageTypeId))
        .Select(x =>
            new
            {
                x.ID,
                x.FileName,
                x.MaxAvailableHeight,
                x.MaxAvailableWidth,
                ImageTypeName = x.ImageType.Name,
                x.FileDescription,
                HasCMYK = (x.CMYKImage != null)
            }
        ).ToList();
}

where 句を追加し、必要な列のみを選択したにもかかわらず、このクエリがまだ遅いことを発見して驚きました。SQL プロファイラーを実行したところ、このクエリが製品のすべての画像のすべての列を取得するように変換さフィルターを実行してインメモリを選択することがわかりました。関連する SQL Server トレースを次に示します。実行に 3541 ミリ秒かかりました (fred.jpg のフィルタリングが行われず、すべての列が返されていることに注意してください)。

exec sp_executesql N'SELECT 
[Extent2].[ID] AS [ID], 
[Extent2].[MimeType] AS [MimeType], 
[Extent2].[ImageTypeId] AS [ImageTypeId], 
[Extent2].[ImageBinary] AS [ImageBinary], 
[Extent2].[ImageThumbnailBinary] AS [ImageThumbnailBinary], 
[Extent2].[FileSizeKb] AS [FileSizeKb], 
[Extent2].[FileName] AS [FileName], 
[Extent2].[FileDescription] AS [FileDescription], 
[Extent2].[MaxAvailableHeight] AS [MaxAvailableHeight], 
[Extent2].[MaxAvailableWidth] AS [MaxAvailableWidth], 
[Extent2].[CMYKImage] AS [CMYKImage], 
[Extent2].[SageStockItemID] AS [SageStockItemID]
FROM  [product].[ProductImages] AS [Extent1]
INNER JOIN [product].[Images] AS [Extent2] ON [Extent1].[ImageID] = [Extent2].[ID]
WHERE [Extent1].[ProductID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=125

Product次に、 and を使用して開始する代わりに.Images.Where、コンテキストから直接開始し、次のように、関心のある製品 ID の where 句を追加することにしました。

var images = ctx.Images.Where(x => (x.Products.Any(y => y.ID == 125)) && (x.FileName == "fred.jpg") && (!imageTypeId.HasValue || x.ImageTypeId == imageTypeId))
    .Select(x =>
        new
        {
            x.ID,
            x.FileName,
            x.MaxAvailableHeight,
            x.MaxAvailableWidth,
            ImageTypeName = x.ImageType.Name,
            x.FileDescription,
            HasCMYK = (x.CMYKImage != null)
        }
    ).ToList();

驚いたことに、これはまさに私が望んでいたとおりに機能しました。以下のように変換された SQL には、必要な列と必要なフィルターのみが含まれています。

exec sp_executesql N'SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[FileName] AS [FileName], 
[Extent1].[MaxAvailableHeight] AS [MaxAvailableHeight], 
[Extent1].[MaxAvailableWidth] AS [MaxAvailableWidth], 
[Extent2].[Name] AS [Name], 
[Extent1].[FileDescription] AS [FileDescription], 
CASE WHEN ([Extent1].[CMYKImage] IS NOT NULL) THEN cast(1 as bit) WHEN ([Extent1].[CMYKImage] IS NULL) THEN cast(0 as bit) END AS [C1]
FROM  [product].[Images] AS [Extent1]
INNER JOIN [dbo].[ImageTypes] AS [Extent2] ON [Extent1].[ImageTypeId] = [Extent2].[ID]
WHERE ( EXISTS (SELECT 
    1 AS [C1]
    FROM [product].[ProductImages] AS [Extent3]
    WHERE ([Extent1].[ID] = [Extent3].[ImageID]) AND (125 = [Extent3].[ProductID])
)) AND (''fred.jpg'' = [Extent1].[FileName]) AND (@p__linq__0 IS NULL OR [Extent1].[ImageTypeId] = @p__linq__1)',N'@p__linq__0 int,@p__linq__1 int',@p__linq__0=1,@p__linq__1=1

SQL Server プロファイラーによると、このクエリの実行には 0 ミリ秒かかりました。

では、ここで何が起こっているのでしょうか? 製品から開始して.Imagesすべてをロードすると、エンティティ データ コンテキストから開始し.Imagesて製品 ID の追加フィルターを使用すると、完全に機能するのでしょうか?

ありがとう!

4

1 に答える 1

0

違いは、最初の例では最初にProduct. 次に、Entity Frameworkに関する限り、あなたはそうします

p.Images

その呼び出しの周りで発生するすべてのことは、EF にとって重要ではありません。これは、EF が常に完全なエンティティ コレクション読み込み、エンティティを完全な状態で具体化するためです。フレーズでImagesフィルタリングされたコレクションはロードされません。また、フレーズにプロパティしかない sWhereは読み込まれません。ImageSelect

2 番目の例では、単一のエンティティが EF によって読み込まれることはありません。これは、プロジェクションのみをフェッチし、完全なエンティティ ( noProductおよび no Image) をフェッチしないためです。DbContext API を使用している場合は、チェックすることで確認できますcontext.Products.Local.Count

エンティティのコレクションからプロジェクションを行う方法があります。あなたの例に従うと、次のようになります。

context.Entry(p).Collection(x => x.Images).Query()
       .Select(x => new
                    {
                        x.ID,
                        x.FileName,
                        ....
于 2013-05-08T14:48:44.427 に答える