C# EF Core-通过键值对的子集合进行过滤非常慢

C# EF Core-通过键值对的子集合进行过滤非常慢,c#,entity-framework,linq-to-sql,entity-framework-core,C#,Entity Framework,Linq To Sql,Entity Framework Core,我的主系统实体用键值对的子集合“标记”,我想用它来过滤主实体的列表。然而,我在下面编写的EF核心查询速度太慢,无法接受 简化实体类 简化查询 第一次使用EF和EF Core时,我对EF有点生疏,但问题在于我使用multiple通过子标记集合进行过滤的方式。当没有指定标记过滤器时,查询的任何命令都能完美执行 我想不出另一种方法来根据选定的标记过滤器对象过滤子标记对象集合-我想,单个过滤器标记会更简单、更快 目前我唯一能想到的选择是自己做一个定制的SQL查询,但在编写我的第一个EF核心查询时,使用这

我的主系统实体用键值对的子集合“标记”,我想用它来过滤主实体的列表。然而,我在下面编写的EF核心查询速度太慢,无法接受

简化实体类

简化查询

第一次使用EF和EF Core时,我对EF有点生疏,但问题在于我使用multiple通过子标记集合进行过滤的方式。当没有指定标记过滤器时,查询的任何命令都能完美执行

我想不出另一种方法来根据选定的标记过滤器对象过滤子标记对象集合-我想,单个过滤器标记会更简单、更快


目前我唯一能想到的选择是自己做一个定制的SQL查询,但在编写我的第一个EF核心查询时,使用这个方法似乎很遗憾

您知道EF Core Any生成的SQL是什么吗?EF Core有一个不幸的设计特性,即如果查询无法转换为SQL,则在客户端静默执行查询

如果您整合了关键和价值测试,该怎么办

或者如果您使用Contains来代替呢


你知道EF Core正在生成什么样的SQL吗?EF Core有一个不幸的设计特性,即如果查询无法转换为SQL,则在客户端静默执行查询

如果您整合了关键和价值测试,该怎么办

或者如果您使用Contains来代替呢


首先要注意的是,您提出的查询不能作为SQL完全计算,因为对于包含非原语值(tagSearchValues)的集合,没有SQL等价物。这将导致EF自动切换到。也就是说,它将满足stuffFilter条件的所有实体及其所有标记都拉入内存,然后应用标记谓词。这显然是没有效率的

第二,查询不准确。包含具有特定键的标记和具有特定值的标记的实体与包含特定键/值组合的标记不同。它需要一个匹配每个组合的查询,如下所示:

db.MainEntities.Where(...)
    .Where(m => tagSearchValues
       .Any(t => m.Tags.Any(mt => mt.Key == t.Key 
                               && mt.Value == t.Value)))
然而,如果您这样做,EF将再次转向低效的客户端评估,您甚至必须自己应用Include或lazy加载来将标记拉入内存。此外,出于某种原因,EF会激发大量冗余查询

事实上,EF和其他ORM一样,并没有很好地适应这样的服务器端成对比较。因此,您需要一个谓词生成器来构建标记谓词。有几个谓词构建器,例如。我用它是因为它很好而且简单。诀窍是:构建一个谓词,并将其应用于以下位置:


我使用或是因为我从您的查询中假设您希望实体在搜索标记中有任何标记。这就是为什么我从PredicateBuilder.True谓词开始,因此如果没有搜索标记,查询将返回结果,与原始查询类似。

首先要注意的是,您提出的查询不能作为SQL完全计算,因为对于包含非原语值TagSearchValue的集合,没有SQL等价物。这将导致EF自动切换到。也就是说,它将满足stuffFilter条件的所有实体及其所有标记都拉入内存,然后应用标记谓词。这显然是没有效率的

第二,查询不准确。包含具有特定键的标记和具有特定值的标记的实体与包含特定键/值组合的标记不同。它需要一个匹配每个组合的查询,如下所示:

db.MainEntities.Where(...)
    .Where(m => tagSearchValues
       .Any(t => m.Tags.Any(mt => mt.Key == t.Key 
                               && mt.Value == t.Value)))
然而,如果您这样做,EF将再次转向低效的客户端评估,您甚至必须自己应用Include或lazy加载来将标记拉入内存。此外,出于某种原因,EF会激发大量冗余查询

事实上,EF和其他ORM一样,并没有很好地适应这样的服务器端成对比较。因此,您需要一个谓词生成器来构建标记谓词。有几个谓词构建器,例如。我用它是因为它很好而且简单。诀窍是:构建一个谓词,并将其应用于以下位置:


我使用或是因为我从您的查询中假设您希望实体在搜索标记中有任何标记。这就是为什么我从PredicateBuilder.True谓词开始,因此如果没有搜索标记,查询将返回结果,与原始查询类似。

Gert,感谢您1000次关于EF的教育,发现标记值和键检查中的错误,并提供完美、简单的解决方案。我只在相对简单的用例中使用过EF,所以以前没有遇到过谓词,但正如您所建议的,它阻止了EF Core不必要执行的几十个select查询逐个查找标记实体。你让这个系统变得活跃起来,让我免于更换EF所需的所有工作
使用自定义SQL进行核心查找-您是一个顶尖的人类!格特活该!是他启发我开始深入研究EF部门的。请注意,EF Core 3.x及更新版本不会自动切换到客户端执行或发出多个SQL调用。相反,如果无法在服务器端执行,或者您可以使用AsEnumerable或类似工具显式切换到客户端执行,它们会引发异常。Gert,感谢您1000次关于EF的教育,发现我的标记值和密钥检查中的错误,并提供完美、简单的解决方案。我只在相对简单的用例中使用过EF,所以以前没有遇到过谓词,但正如您所建议的,它阻止了EF Core不必要执行的几十个select查询逐个查找标记实体。你使这个系统变得活跃起来,并使我免于所有用自定义SQL替换EF核心查找所需的工作——你是一个顶尖的人!格特活该!是他启发我开始深入研究EF部门的。请注意,EF Core 3.x及更新版本不会自动切换到客户端执行或发出多个SQL调用。相反,如果无法在服务器端执行,或者您可以使用AsEnumerable或类似工具显式切换到客户端执行,它们会引发异常。感谢@NetMage的输入,我根据您的建议尝试了几次加速查询,但无法让EF Core停止执行导致问题的数十个冗余查询。如果EF Core正在执行数十个额外查询,则意味着它无法将查询的某些部分转换为SQL并发送。tagSearchValues列表中有多少元素?请注意,EF Core 3.x及更新版本不会自动切换到客户端执行或发出多个SQL调用。相反,如果无法在服务器端执行,或者您可以使用AsEnumerable或类似工具显式切换到客户端执行,则会引发异常。@Matt我知道-但这是在2018年编写的。感谢@NetMage的输入,我根据您的建议尝试了几次加速查询,但无法让EF Core停止执行导致问题的数十个冗余查询。如果EF Core正在执行数十个额外查询,则意味着它无法将查询的某些部分转换为SQL并发送。tagSearchValues列表中有多少元素?请注意,EF Core 3.x及更新版本不会自动切换到客户端执行或发出多个SQL调用。相反,如果服务器端执行不可能,或者您可以使用AsEnumerable或类似工具显式切换到客户端执行,它们会抛出异常。@Matt我知道-但这是在2018年编写的。
(me.Tags.Any(met => tagSearchValues.Any(st => st.Tag == met.Tag && st.Value == met.Value)))
(me.Tags.Select(t => t.Key).Any(tk => tagSearchValues.Select(s => s.Key).Contains(tk))) &&
(me.Tags.Select(t => t.Value).Any(tv => tagSearchValues.Select(s => s.Value).Contains(tv)))
db.MainEntities.Where(...)
    .Where(m => tagSearchValues
       .Any(t => m.Tags.Any(mt => mt.Key == t.Key 
                               && mt.Value == t.Value)))
var tagPredicate = PredicateBuilder.True<MainEntity>();
if (tagSearchValues.Any())
{
    tagPredicate = PredicateBuilder.False<MainEntity>();
    foreach (var tag in tagSearchValues)
    {
        tagPredicate = tagPredicate.Or(m => m.Tags
                           .Any(t => t.Key == tag.Key
                                  && t.Value == tag.Value));
    }
}

var query = _dbContext.MainEntities
    .Where(m => string.IsNullOrWhiteSpace(stuff) || m.Stuff == stuff)
    .Where(tagPredicate);
... // Use query