这是我的模型
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.Children
是 IEnumerable
,所以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/