首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >多OR快速查询与实体框架核心连接表的筛选

多OR快速查询与实体框架核心连接表的筛选
EN

Stack Overflow用户
提问于 2020-06-14 12:05:29
回答 2查看 1.3K关注 0票数 2

这是我的模型

代码语言:javascript
运行
复制
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

对于给定的伴侣列表(父名,子名),我想要得到伴侣(父级,子级),其中如果具有给定名称的父级存在,而不存在子级,则子级可以为空。SQL查询如下所示:

代码语言:javascript
运行
复制
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');

我尝试用实体框架核心来表达这个查询,对于Or和False方法我使用了PredicateBuilder

代码语言:javascript
运行
复制
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包含父级的所有子级,而不是仅包含所需的子级。

我的查询可以用实体框架核心来表示吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-06-18 21:11:04

根据您的意见,现在的要求是:给我所有的父母,指定的名字,并只有一个特定的孩子每个家长,如果有。也就是说:有其他孩子的父母会出现在结果中,但没有孩子。

这听起来很琐碎,但事实并非如此。问题是,它需要两个过滤器,一个是父母的过滤器,一个是孩子的过滤器,在这个过滤器中,孩子的过滤器甚至是父母特有的。SQL查询如下所示:

代码语言:javascript
运行
复制
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查询,父谓词可以是一个简单的Contains,但是您需要使用谓词生成器构建谓词。在这里,出于后面的原因,我使用LINQkit.core

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

代码语言:javascript
运行
复制
var filters = new[]
{
    new { ParentName = "parent1", ChildName = "child1" },
    new { ParentName = "parent2", ChildName = "child2" },
    new { ParentName = "parent3", ChildName = "child3" },
    new { ParentName = "parent4", ChildName = "child5" },
};

并准备谓词:

代码语言:javascript
运行
复制
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中缺少过滤的问题。

代码语言:javascript
运行
复制
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不运行,因此childPredicateStarter隐式转换为Func而不是所需的Expression<Func>>。有关深入的解释,请参见这里

实际工作版本是:

代码语言:javascript
运行
复制
// 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。

票数 2
EN

Stack Overflow用户

发布于 2020-06-14 12:27:26

Parent.Childs包含父级的所有子类,而不是仅包含所需的

过滤包含即将到来,但尚未实现。我有点不知所措,为什么你认为它真的会过滤,因为你的代码明确规定要使所有的孩子.

.Include(p => p.Childs)

手段包括孩子(顺便说一句,孩子英语不好-复数是儿童)。那里没有过滤器。

关于过滤,包括:

对EF核中包含的过滤

在此引用有关部分:

“最后,此功能已从EF Core预览版本5.0.0-preview.3.20181.2开始实施,并将成为EF核心版本5.0.0中的GA。”

但即便如此,您也必须进行过滤(即将ptut过滤到包含中,而不仅仅是告诉它获取所有这些内容)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62372255

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档