【问题标题】:How "where" operates in Linq-to-Entities“哪里”在 Linq-to-Entities 中如何运作
【发布时间】:2013-05-08 22:48:44
【问题描述】:

我在 SQL Server 中有一个 ProductsImages 表,以及一个多对多 ProductImages 链接表,并有一个针对此结构生成的实体框架(VS2012、.NET4.5)模型。

我有一个 WCF 服务,其方法是 GetImageList,它列出了与产品关联的图像。为了速度,我只想从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 Profiler 时,我发现此查询转换为获取产品的 every 图像的 every 列,然后执行过滤器并在内存中选择。这是相关的 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 开始并使用.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,效果很好?

谢谢!

【问题讨论】:

    标签: c#-4.0 entity-framework-4 sql-server-2008-r2


    【解决方案1】:

    不同之处在于,在第一个示例中,您首先加载了Product。然后,就实体框架而言,您可以这样做

    p.Images
    

    围绕该调用发生的所有事情对 EF 都不感兴趣。这是因为 EF 总是加载完整的实体集合并且将实体具体化为完整状态。它不会加载由Where 短语过滤的Images 集合。而且它不会加载只有Select 短语中的属性的Images。

    在第二个示例中,没有单个实体由 EF 加载,因为您只获取投影而没有完整的实体(没有 ProductImage)。如果您在 DbContext API 中,则可以通过检查 context.Products.Local.Count 来验证。

    有一种方法可以从实体的集合中进行投影。按照你的例子,这将是这样的:

    context.Entry(p).Collection(x => x.Images).Query()
           .Select(x => new
                        {
                            x.ID,
                            x.FileName,
                            ....
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多