c# - 使用 Entity Framework Core 对连接表进行多个 OR 的快速查询和过滤

标签 c# sql entity-framework-core

这是我的模型

class Parent 
{ 
   int Id; 
   string Name; 
   List<Child> Childs; 
} // name is unique

class Child 
{ 
    int Id; 
    int ParentId; 
    string Name; 
    Parent Parent; 
} // couple (id, name) is unique

对于给定的夫妻列表( parent 姓名, child 姓名),我想获取夫妻( parent , child ),其中 child 可以为空,如果具有给定名称的父级存在,但子级不存在。 SQL 查询如下所示:

SELECT * 
FROM parents p
LEFT JOIN childs c ON c.parent_id = p.id
WHERE p.name = 'parent1' AND (c.name IS NULL OR c.name = 'child1')
   OR p.name = 'parent2' AND (c.name IS NULL OR c.name = 'child2')
   OR p.name = 'parent3' AND (c.name IS NULL OR c.name = 'child3')
   OR p.name = 'parent4' AND (c.name IS NULL OR c.name = 'child4');

我尝试使用 Entity Framework Core 使用 PredicateBuilder 表达此查询对于 Or 和 False 方法

var predicate = PredicateBuilder.False<Parent>()
    .Or(p => p.Name == "parent1" && p.Childs.Any(c => c.Name == "child1"))
    .Or(p => p.Name == "parent2" && p.Childs.Any(c => c.Name == "child2"))
    .Or(p => p.Name == "parent3" && p.Childs.Any(c => c.Name == "child3"))
    .Or(p => p.Name == "parent4" && p.Childs.Any(c => c.Name == "child4"));

var p = await _db.Parents
    .Include(p => p.Childs)
    .Where(predicate)
    .ToArrayAsync();

这是我能得到的最接近的结果,但这没有得到预期的结果:

  • 如果子项不存在,则父项不存在于结果集中
  • Parent.Childs 包含父级的所有子级,而不仅仅是想要的子级

我的查询可以用 Entity Framework Core 表达吗?

最佳答案

根据您的评论,现在的要求是:给我所有 parent ,按姓名指定,并且每个 parent 只提供一个特定的 child (如果存在)。也就是说:有其他 child 的 parent 将出现在结果中,但没有 child 。

这听起来很微不足道,但事实并非如此。问题是它需要两个过滤器,一个针对 parent ,一个针对 child ,其中子过滤器甚至是特定于 parent 的。 SQL 查询如下所示:

SELECT * 
FROM parents p1
LEFT JOIN 
(
    SELECT ch.*
    FROM children ch
    JOIN parents p2 ON ch.parentid = p2.id
    WHERE (p2.name = 'parent1' AND ch.name = 'child1')
       OR (p2.name = 'parent2' AND ch.name = 'child2')
       OR (p2.name = 'parent3' AND ch.name = 'child3')
       OR (p2.name = 'parent4' AND ch.name = 'child4') -- filter 2
) fc ON fc.parentid = p1.id
WHERE p1.name IN ('parent1','parent2','parent3','parent4') -- filter 1

对于 EF LINQ 查询,父谓词可以是简单的 Contains ,但您希望使用谓词构建器来构建谓词。在这里,出于稍后的原因,我使用 LINQkit.core .

为了能够从一个来源构建谓词,我使用了一个临时结构(但我猜你已经有了类似的东西):

var filters = new[]
{
    new { ParentName = "parent1", ChildName = "child1" },
    new { ParentName = "parent2", ChildName = "child2" },
    new { ParentName = "parent3", ChildName = "child3" },
    new { ParentName = "parent4", ChildName = "child5" },
};

并准备谓词:

using LinqKit;
...
var parentNames = filters.Select(f => f.ParentName).ToList();
var childPredicateStarter = PredicateBuilder.New<Child>();
foreach (var filter in filters)
{
    childPredicateStarter = childPredicateStarter
        .Or(c => c.Parent.Name == filter.ParentName && c.Name == filter.ChildName);
}

现在,理想情况下,LINQ 查询应如下所示( db 是上下文),解决 Include 中缺乏过滤的问题:

var p = db.Parents
    .Where(p => parentNames.Contains(p.Name))
    .Select(p => new
    {
        Parent = p,
        Children = p.Children.Where(childPredicateStarter)
    })
    .AsEnumerable()
    .Select(p => p.Parent);

但这不会运行,因为 p.ChildrenIEnumerable ,所以childPredicateStarter隐式转换为 Func而不是所需的 Expression<Func>> 。请参阅here以获得深入的解释。

实际工作版本是:

// Convert to expression:
Expression<Func<Child, bool>> childPredicate = childPredicateStarter;

var p = db.Parents.AsExpandable() // <-- LINQKit's AsExpandable()
    .Where(p => parentNames.Contains(p.Name))
    .Select(p => new
    {
        Parent = p,
        Children = p.Children.Where(c => childPredicate.Invoke(c))
    })
    .AsEnumerable()
    .Select(p => p.Parent);

AsExpandable调用转换Invoke返回到 EF 可以转换为 SQL 的正确表达式树。

关于c# - 使用 Entity Framework Core 对连接表进行多个 OR 的快速查询和过滤,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62372255/

相关文章:

c# - 如何删除字符串数组中的单词

sql - MS ACCESS 数据库分成一个月

c# - EF Core 中已修改实体的自有类型属性不持久

c# - 如何在Xamarin Forms中查看 'customized'?

c# - ID 的 Crystal 报表

c# - SmoApplication.EnumAvailableSqlServers 不检索本地服务器实例

mysql - 如何在 IN 子句 sql 中使用限制

sql - 我似乎无法在 SQL Server Management Studio 中使用外键或触发器创建看似简单的数据库关系

c# - 在 Entity Framework Core 中添加实体图

c# - 尚未为此 DbContext 配置数据库提供程序以获取 Asp.net Core 中的方法