首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >实体框架:查询子实体

实体框架:查询子实体
EN

Stack Overflow用户
提问于 2011-10-13 18:55:34
回答 4查看 54.9K关注 0票数 47

似乎我无法从db中获取父级及其子级的子集。

例如..。

代码语言:javascript
运行
复制
db.Parents
.Include(p => p.Children)
.Where(p => p.Children.Any(c => c.Age >= 5))

这将返回具有年龄为5+的孩子的所有父母,但是如果我遍历Parents.Children集合,所有的孩子都会出现(不仅仅是那些5岁以上的孩子)。

现在,查询对我来说是有意义的(我已经要求包括孩子,我已经得到他们了!),但是可以想象,在某些场景中,我希望将where子句应用于孩子集合。

如何才能获得每个父母都有一个经过过滤的子代集合(Age>=5)的IEnumerable?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-10-14 21:36:41

要在单个数据库往返中获得具有筛选的子代集合的父代集合,唯一的方法是使用投影。由于Include不支持过滤,所以不能使用Include,它总是加载整个集合。@Daz显示的explicite加载方式需要每个父实体一次往返。

示例:

代码语言:javascript
运行
复制
var result = db.Parents
    .Select(p => new
    {
        Parent = p,
        Children = p.Children.Where(c => c.Age >= 5)
    })
    .ToList();

您可以直接使用此匿名类型对象的集合。(您也可以投影到自己的命名类型,而不是匿名投影(但不是像Parent这样的实体)。)

如果您不禁用更改跟踪(例如,使用AsNoTracking() ),EF的上下文也会自动填充ParentChildren集合。在这种情况下,您可以将父对象投影到匿名结果类型之外(发生在内存中,没有数据库查询):

代码语言:javascript
运行
复制
var parents = result.Select(a => a.Parent).ToList();

对于每个Parentparents[i].Children将包含您过滤的子项。

编辑到问题中的最后一次编辑:

我想要的是a)孩子超过5岁的父母名单(只包括这些孩子)。

上面的代码将返回所有父代,并仅包括Age >= 5的子代,因此,如果只有Age < 5的子代,则可能也包括具有空的子代集合的父代。您可以对父代使用附加的Where子句来筛选出这些子代,以便仅获取至少具有一个(Any)子代且Age >= 5的父代:

代码语言:javascript
运行
复制
var result = db.Parents
    .Where(p => p.Children.Any(c => c.Age >= 5))
    .Select(p => new
    {
        Parent = p,
        Children = p.Children.Where(c => c.Age >= 5)
    })
    .ToList();
票数 52
EN

Stack Overflow用户

发布于 2011-10-13 20:00:22

以你的例子为例,下面的代码应该能做你需要的事情。请查看here以获取更多信息。

代码语言:javascript
运行
复制
db.Entry(Parents)
.Collection("Children")
.Query().Cast<Child>()
.Where(c => c.Age >= 5))
.Load();
票数 3
EN

Stack Overflow用户

发布于 2011-10-14 20:57:41

我认为父母和孩子并不是真正适合作为单独的实体。孩子也可以是父母,而且孩子通常有两个父母(一个父亲和一个母亲),所以这不是最简单的上下文。但我假设您只有一个简单的1:n关系,就像我使用的下面的主从模型一样。

你需要做的是做一个left outer join (这个答案让我走上了正确的道路)。这样的连接有点棘手,但下面是代码

代码语言:javascript
运行
复制
var query = from m in ctx.Masters
            join s in ctx.Slaves
              on m.MasterId equals s.MasterId into masterSlaves
            from ms in masterSlaves.Where(x => x.Age > 5).DefaultIfEmpty()
            select new {
              Master = m,
              Slave = ms
            };

foreach (var item in query) {
  if (item.Slave == null) Console.WriteLine("{0} owns nobody.", item.Master.Name);
  else Console.WriteLine("{0} owns {1} at age {2}.", item.Master.Name, item.Slave.Name, item.Slave.Age);
}

这将转换为以下使用EF 4.1的SQL语句

代码语言:javascript
运行
复制
SELECT 
[Extent1].[MasterId] AS [MasterId], 
[Extent1].[Name] AS [Name], 
[Extent2].[SlaveId] AS [SlaveId], 
[Extent2].[MasterId] AS [MasterId1], 
[Extent2].[Name] AS [Name1], 
[Extent2].[Age] AS [Age]
FROM  [dbo].[Master] AS [Extent1]
LEFT OUTER JOIN [dbo].[Slave] AS [Extent2]
ON ([Extent1].[MasterId] = [Extent2].[MasterId]) AND ([Extent2].[Age] > 5)

请注意,在联接的集合上而不是在from和select之间对年龄执行额外的where子句非常重要。

编辑:

如果需要分层结果,可以通过执行分组来转换平面列表:

代码语言:javascript
运行
复制
var hierarchical = from line in query
                   group line by line.Master into grouped
                   select new { Master = grouped.Key, Slaves = grouped.Select(x => x.Slave).Where(x => x != null) };

foreach (var elem in hierarchical) {
   Master master = elem.Master;
   Console.WriteLine("{0}:", master.Name);
   foreach (var s in elem.Slaves) // note that it says elem.Slaves not master.Slaves here!
     Console.WriteLine("{0} at {1}", s.Name, s.Age);
}

请注意,我使用匿名类型来存储分层结果。当然,您也可以创建一个特定类型,如下所示

代码语言:javascript
运行
复制
class FilteredResult {
  public Master Master { get; set; }
  public IEnumerable<Slave> Slaves { get; set; }
}

然后将组投影到该类的实例中。如果您需要将这些结果传递给其他方法,这会更容易。

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

https://stackoverflow.com/questions/7753039

复制
相关文章

相似问题

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