c# - Enumerable.Any() 在大型 Linq 数据集上非常慢

标签 c# .net entity-framework linq entity-framework-core

我正在运行这样的代码:

var somethings = db.Somethings.Select(s => new SomethingViewModel
{
    Id = s.Id,
    Name = s.Name,
    IsActive = s.IsActive
    SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel
    {
        Id = ss.Id,
        Name = ss.Name,
        IsActive = ss.IsActive
    }).Where(wss => wss.IsActive)                        
}).Where(ws => ws.IsActive && (ws.SubSomethings.Any())) //remove elements if no SubSomethings
.ToList();

如您所见,这是一个一对多的关系。 Something 中有一个 SubSomethings 列表。如果我取出 && (ws.SubSomethings.Any()),我会很快返回一个列表。

但是,我只想在列表中包含至少具有一个 SubSomething 的 Something。我还尝试了以下方法并获得了同样可怕的效率:

var somethings = db.Somethings.Select(s => new SomethingViewModel
{
    Id = s.Id,
    Name = s.Name,
    IsActive = s.IsActive
    SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel
    {
        Id = ss.Id,
        Name = ss.Name,
        IsActive = ss.IsActive
    }).Where(wss => wss.IsActive)                        
}).Where(ws => ws.IsActive)
.ToList(); //this finishes very quickly

var somethings2 = somethings.Where(s => s.SubSomethings.Any()).ToList(); //This is where the code bogged down

如何重写我的查询来使陷入困境的代码更快?需要注意的一件事:这适用于一两个记录。当我击中 >8000 条记录时,至少需要四分钟。

这里是我在SubSomething表上为SomethingId的外键创建的Index,对应Something.Id

CREATE NONCLUSTERED INDEX [IX_SubSomething_SomethingId] ON [dbo].[SubSomething]
(
    [SomethingId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO

这是 SubSomething.SomethingId 的外键创建:

ALTER TABLE [dbo].[SubSomething]  WITH CHECK ADD  CONSTRAINT [FK_SubSomething_Something_SomethingId] FOREIGN KEY([SomethingId])
REFERENCES [dbo].[Something] ([Id])
GO

ALTER TABLE [dbo].[SubSomething] CHECK CONSTRAINT [FK_SubSomething_Something_SomethingId]
GO

最佳答案

EF Core 是您的问题。目前已知当查询包含子集合投影时执行 N + 1 个子查询。

解决它并将整个事情限制为 2 个 SQL 查询的唯一方法是尽可能多地进行过滤,然后使用预先加载将整个实体集和子实体加载到内存中,然后切换到 LINQ to Objects 并执行最后的投影/过滤:

var somethings = db.Somethings
    .Include(s => s.SubSomethings)
    .Where(s => s.IsActive)
    .AsEnumerable()
    .Select(s => new SomethingViewModel
    {
        Id = s.Id,
        Name = s.Name,
        IsActive = s.IsActive,
        SubSomethings = s.SubSomethings.Select(ss => new SubSomethingViewModel
        {
            Id = ss.Id,
            Name = ss.Name,
            IsActive = ss.IsActive
        }).Where(wss => wss.IsActive).ToList()
    })
    .Where(s => s.SubSomethings.Any())
    .ToList();

关于c# - Enumerable.Any() 在大型 Linq 数据集上非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43812336/

相关文章:

c# - 将 Delphi DLL 与来自 C# 的动态数组一起使用

C# XSLT 转换内存不足

c# - LINQ to Entities 无法识别方法 'Boolean Exists(System.Predicate` 1[Entities.Connection])' 方法

entity-framework - 将 Entity Framework 与 SQL Compact 私有(private)安装一起使用

c# - UWP 应用程序更快地加载图像?

c# - 在 C# 单元测试中实现未处理的异常处理程序

c# - 如何在 C# .NET 中像 Java 代码一样加密

c# - SQLite 与 Entity Framework

c# - 在 linq foreach 中调用方法 - 有多少开销?

c# - 如果我将引用项目的 Copy Local 设置为 false,则主项目找不到程序集