似乎我无法从db中获取父级及其子级的子集。
例如..。
db.Parents
.Include(p => p.Children)
.Where(p => p.Children.Any(c => c.Age >= 5))
这将返回具有年龄为5+的孩子的所有父母,但是如果我遍历Parents.Children集合,所有的孩子都会出现(不仅仅是那些5岁以上的孩子)。
现在,查询对我来说是有意义的(我已经要求包括孩子,我已经得到他们了!),但是可以想象,在某些场景中,我希望将where子句应用于孩子集合。
如何才能获得每个父母都有一个经过过滤的子代集合(Age>=5)的IEnumerable?
发布于 2011-10-14 13:36:41
要在单个数据库往返中获得具有筛选的子代集合的父代集合,唯一的方法是使用投影。由于Include
不支持过滤,所以不能使用Include
,它总是加载整个集合。@Daz显示的explicite加载方式需要每个父实体一次往返。
示例:
var result = db.Parents
.Select(p => new
{
Parent = p,
Children = p.Children.Where(c => c.Age >= 5)
})
.ToList();
您可以直接使用此匿名类型对象的集合。(您也可以投影到自己的命名类型,而不是匿名投影(但不是像Parent
这样的实体)。)
如果您不禁用更改跟踪(例如,使用AsNoTracking()
),EF的上下文也会自动填充Parent
的Children
集合。在这种情况下,您可以将父对象投影到匿名结果类型之外(发生在内存中,没有数据库查询):
var parents = result.Select(a => a.Parent).ToList();
对于每个Parent
,parents[i].Children
将包含您过滤的子项。
将编辑到问题中的最后一次编辑:
我想要的是a)孩子超过5岁的父母名单(只包括这些孩子)。
上面的代码将返回所有父代,并仅包括Age
>= 5的子代,因此,如果只有Age
< 5的子代,则可能也包括具有空的子代集合的父代。您可以对父代使用附加的Where
子句来筛选出这些子代,以便仅获取至少具有一个(Any
)子代且Age
>= 5的父代:
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();
发布于 2011-10-13 12:00:22
以你的例子为例,下面的代码应该能做你需要的事情。请查看here以获取更多信息。
db.Entry(Parents)
.Collection("Children")
.Query().Cast<Child>()
.Where(c => c.Age >= 5))
.Load();
发布于 2011-10-14 12:57:41
我认为父母和孩子并不是真正适合作为单独的实体。孩子也可以是父母,而且孩子通常有两个父母(一个父亲和一个母亲),所以这不是最简单的上下文。但我假设您只有一个简单的1:n关系,就像我使用的下面的主从模型一样。
你需要做的是做一个left outer join (这个答案让我走上了正确的道路)。这样的连接有点棘手,但下面是代码
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语句
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子句非常重要。
编辑:
如果需要分层结果,可以通过执行分组来转换平面列表:
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);
}
请注意,我使用匿名类型来存储分层结果。当然,您也可以创建一个特定类型,如下所示
class FilteredResult {
public Master Master { get; set; }
public IEnumerable<Slave> Slaves { get; set; }
}
然后将组投影到该类的实例中。如果您需要将这些结果传递给其他方法,这会更容易。
https://stackoverflow.com/questions/7753039
复制