entity framework:查询子实体
我正在学习关于entity framework,并有问题!
有人可以澄清,如果我认为我无法从数据库中获得父母及子女的子女,是否正确?
例如…
db.Parents .Include(p => p.Children) .Where(p => p.Children.Any(c => c.Age >= 5))
这将返回所有有5岁以上孩子的父母,但是如果我重复通过Parents.Children收集,所有的孩子将在场(不只是5岁以上)。
现在查询确实对我有意义(我已经要求包括孩子,我已经得到了他们!),但是可以想象,在某些情况下,我希望将where子句应用于子集合。
问题:
- 我说的是正确的吗?
- 是否有可能得到父母,只是从数据库的一个子集,而不需要调用数据库的负载?
- 我的方式离开的标志? (不会是第一次)!
我发现了一些关于这个主题的博客和SOpost,但是没有什么能够解释我的小脑袋。
编辑
读过这个博客 (感谢达斯·刘易斯)…….我还是不明白!
在博客中给出的例子中,我可以看到如何通过一个Parent实例来实现它,但是我正在努力研究如何使用集合实现它。
我怎么能得到一个IEnumerable,其中每个父母都有一个过滤收集的儿童(年龄> = 5)?
进一步澄清:
在回答唐安德鲁的评论时,我在a)一个有5岁以上孩子的父母名单(只包括那些孩子)。
任何帮助感激,
谢谢。
在单个数据库往返中获取带过滤子集合的父母的唯一方法是使用投影。 由于不支持过滤,因此不可能使用加载( Include
), Include
始终加载整个集合。 @Daz显示的显式加载方式每个父实体需要一次往返。
例:
var result = db.Parents .Select(p => new { Parent = p, Children = p.Children.Where(c => c.Age >= 5) }) .ToList();
您可以直接使用这个匿名types对象的集合。 (您也可以投影到您自己的命名types中,而不是匿名投影(但不是像Parent
类一样)。)
如果您AsNoTracking()
用更改跟踪(例如,使用AsNoTracking()
,则EF的上下文也将自动填充Parent
的Children
集合。 在这种情况下,您可以将父项从匿名结果types中移出(发生在内存中,没有数据库查询):
var parents = result.Select(a => a.Parent).ToList();
parents[i].Children
孩子将包含您为每个Parent
过滤的孩子。
编辑到您的最后编辑的问题:
我在a)一个有5岁以上孩子的父母名单(只包括那些孩子)。
上面的代码将返回所有父母,只包括Age
> = 5的孩子,如果只有Age
小于5岁的孩子,那么可能还会有一个空子女collections的父母。您可以使用父母的附加Where
子句过滤这些父母只得到至less有一个 ( 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();
以你的榜样为例,以下应该做你需要的。 在这里看看更多的信息。
db.Entry(Parents) .Collection("Children") .Query().Cast<Child>() .Where(c => c.Age >= 5)) .Load();
我觉得父母和孩子不是很适合作为独立的实体。 一个孩子总是可以成为父母,通常一个孩子有两个父母(父亲和母亲),所以这不是最简单的情况。 但是我假设你只是有一个简单的1:n关系,就像我使用的以下主从模型一样。
你需要做的是做一个左外连接 (这个答案已经使我走上了正确的道路)。 这样的连接有点棘手,但是这里是代码
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)
请注意,在join的集合上执行附加的where子句是非常重要的,而不是from和select之间的。
编辑:
如果您需要分层结果,则可以通过执行分组来转换平面列表:
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); }
请注意,我使用匿名types来存储分层结果。 你当然也可以创build一个这样的特定types
class FilteredResult { public Master Master { get; set; } public IEnumerable<Slave> Slaves { get; set; } }
然后将该组投影到此类的实例中。 如果您需要将这些结果传递给其他方法,则更容易。