实体框架 Sum() 性能

本文关键字:性能 Sum 框架 实体 | 更新日期: 2023-09-27 18:33:03

我在使用以下实体框架查询时遇到性能问题:

using (MyEntities context = new MyEntities())
{
    return context.Companies
                  .Single(c => c.CompanyId == company.CompanyId)
                  .DataFile.Sum(d => d.FileSize);
}

在 SQL 探查器中跟踪时,我看到以下 SQL 命令:

exec sp_executesql N'SELECT 
[Extent1].[DataFileID] AS [DataFileID], 
[Extent1].[LocalFileName] AS [LocalFileName], 
[Extent1].[ServerFileName] AS [ServerFileName], 
[Extent1].[DateUploaded] AS [DateUploaded], 
[Extent1].[FileSize] AS [FileSize], 
[Extent1].[CompanyID] AS [CompanyID]
FROM [dbo].[DataFile] AS [Extent1]
WHERE [Extent1].[CompanyID] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=16

据我所知,所有数据文件行都被返回到内存中(超过 10,000 行(,然后发生Sum()

编辑:

根据帕特里克的建议,我已将查询更改为:

using (MyEntities context = new MyEntities())
{
    return context.Companies
                  .Where(c => c.CompanyId == company.CompanyId)
                  .Select(x => x.DataFiles.Sum(d => d.FileSize))
                  .Single();
}

SQL 跟踪如下所示:

SELECT TOP (2) 
(
    SELECT 
        SUM([Extent2].[FileSize]) AS [A1]
    FROM 
        [dbo].[DataFile] AS [Extent2]
    WHERE 
        [Extent1].[CompanyId] = [Extent2].[CompanyID]
) AS [C1]
FROM 
    [dbo].[Company] AS [Extent1]
WHERE 
    [Extent1].[CompanyId] = 16

这要好得多,但是,本质上我只想要这样简单快捷的东西:

SELECT SUM(FileSize) FROM DataFile WHERE CompanyId = 16

实体框架 Sum() 性能

首先...自我上次检查以来,任一实体框架都有所改进。表达式 .Single(c => c.CompanyId == company.CompanyId) 应该在所有帐户中失败,因为实体框架应该在 Expression.Constant<Company> 上失败。我怀疑实际上你已经混淆了你的代码列表。

这有点错误的原因是由于.Single(Expression)的工作方式。与大多数 Linq IQueryable<T> 扩展方法不同,它立即进行评估。

using (MyEntities context = new MyEntities())
{
    return context.Companies
                  .Single(c => c.CompanyId == company.CompanyId)
                  .DataFile.Sum(d => d.FileSize);
}

相当于

using (MyEntities context = new MyEntities())
{
    Company company = context.Companies.Single(c => c.CompanyId == company.CompanyId);
    List<DataFile> dataFiles = company.DataFile
    return dataFiles.Sum(d => d.FileSize);
}

为您分解一下。性能不佳来自多个点。

首先是.Single()强制计算查询,返回Company(您正在追求,但不需要(。如果幸运的话,EF 可能很聪明,只需从缓存中提取它。

第二行提取该公司的所有数据文件(因为List<T>其中没有任何实体框架代码。这意味着它必须下拉整个列表。

然后您知道的第三部分是.Sum().但是如果你检查实际的.Sum()实现,它实际上是IEnumerable.Sum(),这与实体框架无关。签名完全不同。

与 ELinq 一起使用的那个是IQueryable<T>.Sum<T,TValue>(Expression<Func<T,TValue>> projection),Linq to Object 是IEnumerable<T>.Sum<T,TValue>(Func<T.TValue> projection)

TLDR:

简而言之,需要一些时间来了解 LinqToEF 的起点和终点。代码工作的唯一原因是 EF 延迟加载。但我建议您在存在性能问题时启用 EF 延迟加载,因为它通常会掩盖对 Linq 的不良理解。

select 语句实际上应该只返回查询定义的内容,

select * from Extend1 where CompanyID = 16

意思是,不,它应该只返回公司ID = 16的所有行。

Tbh 不知道实体框架的行为方式,但是如果您使用 nhibernate 执行查询,例如,linq 查询.First(p=>p.Id==16)将执行 Select Top(1(

也许这篇文章可以帮助您优化生成的查询:强制实体框架使用 SQL 参数化以实现更好的 SQL 过程缓存重用