c# - 将 Include() 调用替换为 Select()

标签 c# entity-framework iqueryable

我试图消除在此 IQueryable 定义中使用 Include() 调用:

return ctx.timeDomainDataPoints.AsNoTracking()
   .Include(dp => dp.timeData)
   .Include(dp => dp.RecordValues.Select(rv => rv.RecordKind).Select(rk => rk.RecordAlias).Select(fma => fma.RecordAliasGroup))
   .Include(dp => dp.RecordValues.Select(rv => rv.RecordKind).Select(rk => rk.RecordAlias).Select(fma => fma.RecordAliasUnit))
   .Where(dp => dp.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
   .Where(dp => dp.Source == 235235)
   .Where(dp => dp.timeData.time >= start && cd.timeData.time <= end)
   .OrderByDescending(cd => cd.timeData.time);

我一直遇到数据库问题,运行时间太长,其主要原因是 Include() 调用正在拉取所有内容。 从生成的 SQL 查询返回的表中可以明显看出这一点,该查询显示返回了许多不必要的信息。 我猜你学到的东西之一。 数据库拥有大量数据点,其中有许多记录值。 每个记录值都映射到可能具有记录别名的记录类型。

我尝试创建一个 Select() 作为替代方案,但我只是不知道如何构建正确的 Select 并保持正确加载实体层次结构。 IE。相关实体加载了对数据库的不必要的调用。

是否有人有替代解决方案可以帮助我解决这个问题。

如果需要,我会添加更多细节。

最佳答案

你是对的。数据库查询较慢的部分之一是将所选数据从 DBMS 传输到本地进程。因此限制这一点是明智的。

每个TimeDomainDataPoint都有一个主键。此 TimeDomainDataPoint 的所有 RecordValues 都有一个外键 TimeDomainDataPointId,其值等于此主键。

因此,如果 ID 为 4 的 TimeDomainDataPoint 有一千个 RecordValue,那么每个 RecordValue 都会有一个值为 4 的外键。将这个值 4 传输 1001 次将是一种浪费,而您只需要一次。

When querying data, always use Select and select only the properties you actually plan to use. Only use Include if you plan to update the fetched included items.

以下操作会更快:

var result = dbContext.timeDomainDataPoints
    // first limit the datapoints you want to select
    .Where(datapoint => d.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))
    .Where(datapoint => datapoint.Source == 235235)
    .Where(datapoint => datapoint.timeData.time >= start
                     && datapoint.timeData.time <= end)
    .OrderByDescending(datapoint => datapoint.timeData.time)

    // then select only the properties you actually plan to use
    Select(dataPoint => new
    {
        Id = dataPoint.Id,
        RecordValues = dataPoint.RecordValues
            .Where(recordValues => ...)           // if you don't want all RecordValues
            .Select(recordValue => new
            {
                // again: select only the properties you actually plan to use:
                Id = recordValue.Id,
                // not needed, you know the value: DataPointId = recordValue.DataPointId,
                RecordKinds = recordValues.RecordKinds
                    .Where(recordKind => ...) // if you don't want all recordKinds
                    .Select(recordKind => new
                    {
                         ... // only the properties you really need!
                    })
                    .ToList(),
                 ...
            })
            .ToList(),

        TimeData = dataPoint.TimeData.Select(...),
        ...
    });

可能的改进

部分:

.Where(datapoint => d.RecordValues.Any(rv => rv.RecordKind.RecordAlias != null))

用于仅获取具有非空 RecordAlias 的 recordValues 的数据点。如果您无论如何都选择 RecordAlias,请考虑在选择后执行此操作:

.Select(...)
.Where(dataPoint => dataPoint
       .Where(dataPoint.RecordValues.RecordKind.RecordAlias != null)
       .Any());

我不太确定这是否更快。如果您的数据库管理系统在内部首先创建一个包含所有联接表的所有列的完整表,然后丢弃未选择的列,那么它不会产生任何影响。但是,如果它只创建一个包含其实际使用的列的表,那么内表会更小。这可能会更快。

关于c# - 将 Include() 调用替换为 Select(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54512948/

相关文章:

c# - 如何在 Silverlight 单元测试框架中记录输出?

c# - 将值从窗口返回到 WPF 类

c# - 我们可以用 DllImport 方法签名中的 "int"替换所有 "IntPtr"参数和返回类型吗?

entity-framework - EF 的列名无效

c# - LINQ - 重构。包含对 IEnumerable<string> 的比较以区分大小写

c# - 组合表达式<Func<T, bool>> 谓词

c# - 自定义测试适配器未在测试资源管理器上显示测试

c# - SQL 查询在代码中超时,但在管理工作室中需要几秒钟

c# - 使用 IQueryable 查找另一个坐标的半径内的点

c# - 此代码中返回相同结果但不同 SQL 的 LINQ 方法的执行顺序。里面到底发生了什么?