我有一个使用EF 6和MVC 5的应用程序,可以很好地输入数据,但现在当我尝试显示其中的一些时,我遇到了麻烦。我的实体的基本布局如下图所示:
我遇到问题的第一部分是查询和过滤数据。我想返回一份存在调查和签收的处所和相关数据清单,但批准却没有。在直接SQL中,现在可以使用的查询是:
SELECT *
FROM Premises p LEFT OUTER JOIN Approvals a ON a.Id = p.Id
JOIN Surveys s ON s.PremiseId = p.Id
JOIN SignOffs so ON so.Id = s.Id
WHERE a.ApprovedBy IS NULL
我开始使用的代码是这样的:
var premises = Premises.Include(p => p.Approval)
.Include(p => p.Surveys)
.Include(p => p.Surveys.Select(s => s.SignOff));
此出现 *以返回包括子数据在内的所有记录,但是当我尝试过滤它时,我只获得有签收记录但没有批准的记录,它没有&#39工作。
var premises = Premises.Include(p => p.Approval).Where(p => p.Approval.ApprovedBy == null)
.Include(p => p.Surveys)
.Include(p => p.Surveys.Select(s => s.SignOff).Where(s => s.Signature != null));
如果我使用此代码,我会收到此错误:
Include路径表达式必须引用在类型上定义的导航属性。使用虚线路径作为参考导航属性,使用Select运算符作为集合导航属性。 参数名称:路径
我已经改变了这个问题以尝试不同的事情,所以我不确定我做了什么,但我认为第一个Where
语句本身可能有用,但是第二个肯定会导致错误。
我如何构建我的查询以使其返回正确过滤的请求数据?
另外,我在上面加上一个星号表示查询出现以返回所有数据和子数据,因为我无法对其进行实际测试。当我试图为此编写我的Razor CSHTML页面时,它没有给我儿童和孙子数据的智能感知,如果我输入我认为它应该是我会得到错误。如何在页面上引用此数据?
答案 0 :(得分:1)
您不能像这样使用Include()
,它只适用于指定加载导航属性,而不是指定在导航属性为某事时加载实体(在您的情况下不为null)。
要进行过滤,我建议这样:
var premises = Premises.Include(p => p.Approval).Include(p => p.Surveys).Include(p => p.Surveys.Select(s => s.SignOff))
.Where(p=>p.Approval.ApprovedBy!=null && p.Surveys.Any(s=>s.SignOff.Signature!=null));
基本上,包含和过滤与彼此无关。使用includes,您只需指定要加载的内容,您仍然可以在原始实体集上使用过滤器。
答案 1 :(得分:1)
你混淆了Include
LINQ方法的作用。它只告诉EF热切地加载这种关系,如果你的查询本身利用了这种关系,这实际上是不必要的;在这种情况下,EF将默认包含关系。
它没有做的是允许你过滤这些关系。例如,在代码的这一部分中:
.Include(p => p.Surveys.Select(s => s.SignOff).Where(s => s.Signature != null));
where子句适用于Premises
,不是 SignOff
,您似乎在想。换句话说,Where
过滤了要查询的主表格,而不是您所包含的表格。
这里有两条前进的道路。您只需按重要部分过滤Premises
,即:
var premises = Premises.Where(p => p.Approval.ApprovedBy == null && p => p.Surveys.Any(s => s.SignOff.Signature != null));
这将仅返回这些条件为真的前提,但所包含的Surveys
集合将包含与每个前提相关的所有调查,而不仅仅是具有空签收签名的调查。
如果您还需要过滤相关项目,则必须明确加载它们:
foreach (premise in premises)
{
context.Entry(premise)
.Collection(p => p.Surveys)
.Query()
.Where(s => s.SignOff.Signature != null)
.Load();
}
有两点需要注意:
由于必须如何应用此查询的性质,因此无法对所有前提执行此操作。您必须遍历该处所并明确加载每个Surveys
集合。
由于这会发出新查询,因此您希望避免在此明显加载之前懒惰或急切地加载Surveys
集合。否则,您需要两次查询相同的信息,这是非常低效的。确保这一点的最简单方法是从集合属性中删除virtual
关键字。但是,如果您这样做,那么您将不得不急于或显式加载该集合,否则它将为null。有关详细信息,请参阅:https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx