Entity Framework 包含 Where

6
如果我的查询看起来像这样:
var forms = repo.GetForms().Where(f => f.SubForms.Any(sf => sf.Classes.Any(c => c.TermId == termId)));

从这里你可以看到我的架构如下:
SubForm 有许多 Class,每个 Class 又有许多 Term。
我想要的是:
在特定的 Term 中,所有 SubForms 及其 Classes。
现在发生的事情是,我得到了所有具有特定 Term 中任何 Class 的 SubForms。这意味着 SubForm 返回了所有子类,而不仅仅是与 Term 相关的那些。
例如:我有两个术语,一个 SubForm 每个术语有 2 个 classes。这个查询会带回 4 个 classes 而不是那个特定术语中的 2 个。
是否有任何 Include('Expression') 可以用来表示我只想基于条件包含所有 classes?或者我的查询有问题吗?

第一个 Any 应该是 Where,对吧?否则你的查询只会返回一个布尔值,表示“是否有任何子表单具有给定 termId 的术语类别?” - Slauma
@Slauma 抱歉,还有另一个表达式的上一级,我会编辑它,抱歉。 - Shawn Mclean
5个回答

6
请使用以下内容:
var subForms = repo.GetSubForms.Select(sf = new {
        SubForm = sf,
        Classes = sf.Classes.Where(c => c.TermId == termId)
    }).ToList()
    .Select(t => t.SubForm)
    .ToList();

更新: 根据@Slauma的评论:

如果您想加载具有任何TermClassSubForm,可以从末尾开始进行操作,例如:

var subForms = repo.Terms.Where(t => t.Id == termId).Select(t => new {
        Term = t,
        Class = t.Class,
        SubForm = t.Class.SubForm
    }).ToList()
    .Select(t => t.SubForm).ToList();

如果您想用最简单的方式实现,您可以在Term上使用Include,如下:

OR(或):

var subForms = repo.Terms.Include("Class.SubForm").Where(t => t.Id == termId)
                   .Select(t => t.Class.SubForm).ToList();

注意: 根据你的问题,我理解你有以下关系:

SubForm has_many Class has_many Term

但是,您提供的代码显示的关系如下:
SubForm has_many Class
Term has_many Class

如果可以的话,请将您的实体置于问题中或更详细地解释它们之间的关系。谢谢。

1
你仍然需要在 GetSubFormsSelect 之间添加过滤器:Where(sf => sf.Classes.Any(c => c.TermId == termId))。否则,你将会从数据库中加载 所有 子表单,包括没有匹配 termId 的类的子表单。 - Slauma
@Slauma 好的,实际上我并没有完全理解这个问题;所以我更新了我的答案;谢谢 :) - amiry jd
1
确实,问题中的第一个“Any”有些令人困惑。我在问题下面刚写了一条评论,因为我相信他实际上是指“Where”。 - Slauma
@Javad_Amiry,我已经编辑了问题并增加了复杂性。 - Shawn Mclean
@Lolcoder,你可以使用“从结尾到开头”的模式;我现在没有Win和VS来测试代码,请测试一下并告诉我是否有任何问题。 - amiry jd

4

Include(Where Expression)不存在。如果您在使用Include时进行急切加载,那么将始终加载所有元素。

有一种方法可以绕过这个问题,就是使用投影。基本思路是选择一个新的匿名类型,并具有您想要的属性和另一个带有过滤导航项的属性。 EF将它们链接在一起,因此您将伪造一个Include(Where ... )

请参考此处的示例


1
小问题:Include(Expression) 作为 System.Data.Entity.DbExtensions.Include 扩展方法确实存在,但其含义完全不同。它被用作字符串字面量的替代方式:repo.Terms.Include(term => term.Classes) - user743382
谢啦。我修改了我的回答。 - Wouter de Kort

1
有时候我会陷入花哨的LINQ扩展方法中,试图弄清楚如何急切地加载我想要的内容,最终只能采用非常简单的“join”概念。
var result = 
(from f in SubForums
from c in Classes
from t in Term
where t.TermId = 1
select new { SubForum = f, Class = c, Term = t }).ToList();

这是一个简单的连接,使用预定义的导航属性(因此您不必指定连接条件)。您返回一个匿名类型,其中包含您需要的所有内容。这样做的美妙之处在于,Entity Framework将为您执行自动修复,因此如果您希望,您可以仅从方法中返回SubForum,它将自动包含Class和随后的Term引用。


0

我不知道关系的确切名称,但应该是类似于以下内容:

repo.Terms
    .Include("Classes")
    .Include("Classes.SubForms")
    .SingleOrDefault(x => x.TermId = termId);

// or 

repo.GetSubForms
    .Include("Classes")
    .Where(sf => sf.Classes.Where(c => c.TermId == termId));

0

这似乎是一个常见的请求,当我在今年早些时候寻找解决方案时,很难找到一个。最终我使用了下面链接中包含的解决方案(我不确定这是否是我找到的确切解决方案,但它是相同的思路)。希望这可以帮助到你!

在Entity Framework查询中过滤“Includes”表

                //Found this method to filter our child objects instead of using .include()
                var Results = (from res in
                                   (from u in DataContext.User
                                    where u.Type.ToUpper() != "ADMIN"
                                    && u.StartDate <= DateTime.Now
                                    && (u.EndDate == null || u.EndDate >= DateTime.Now)
                                    select new
                                    {
                                        User = u,
                                        Access = u.Access.Where(a => a.StartDate <= DateTime.Now
                                                  && (a.EndDate == null || a.EndDate >= DateTime.Now))
                                    }
                                    )
                               select res);

                //The ToArray is neccesary otherwise the Access is not populated in the Users
                ReturnValue = Results.ToArray().Select(x => x.User).ToList();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接