多対多のリンク テーブルと共に SQL Server に and テーブルがありProducts
、この構造に対して生成された Entity Framework (VS2012、.NET4.5) モデルがあります。Images
ProductImages
GetImageList
製品に関連付けられた画像を一覧表示するメソッドを持つ WCF サービスがあります。速度を上げるために、テーブルからいくつかの列のみを返したいと思います。特に、高解像度の画像を格納するために非常に大きくなる可能性のあるおよび列をImages
除外します。ImageBinary
ImageThumbnailBinary
クエリの要点を証明するために、ファイル名が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 の追加フィルターを使用すると、完全に機能するのでしょうか?
ありがとう!