我有这个LINQ查询,它在Include中的过滤器上给出了错误。 在搜索我的朋友Google时,我发现无法在“包含”中进行过滤。我已经找到了一些方法来实现这个目的,但我不能让它适用于我的具体案例。
return context.Timesheets.Where(t => t.UserId == userId && t.SubjectDate == date && t.WorkingDaySchedules.Count() > 0)
.Include(t => t.Project)
.Select(t => t.Project)
.Include(p => p.Account)
.Include(pc => pc.ProjectConsultants.Where(c => c.UserId == userId));
这是让我头疼的最后一个包括:) 有人知道怎么做吗?
答案 0 :(得分:1)
我觉得有些事情需要改进。
这不是正确的地方。
Include
旨在自动检索所有链接的实体。由于您不希望所有实体(您只想要一个子集),因此您不应该使用Include
。
或者,您仍然可以使用Include
,只要您乐意删除不需要的条目在内存中(即在加载后)。但我认为你不想要那个。
相反,您可以使用显式Select
语句。举个简单的例子:
context.Projects
.Where(p => p.Id == projectId)
.Select(p => new ConsultantSearchResult() {
Project = p,
ConsultantsNamedBob = p.Consultants.Where(c => c.FirstName == "Bob")
}).ToList();
请注意缺少Include
。正如我之前所说,Include
用于自动(并隐式)加载相关数据。但是因为你明确地在Select
中陈述了你想要的数据,所以不再需要隐式包含。 EF将为您提供您所要求的。
Select
不直观我认为你期待的不同于你所获得的东西。看代码:
return context.Timesheets //1
.Where(...) //2
.Select(t => t.Project) //3
看看会发生什么:
如果您的过滤(第2步)为您提供了来自同一项目的多个时间表,那么.Select(t => t.Project)
将为您提供同一项目的多个实例。那并不好。
这里有两个例外:
First
,Single
,FirstOrDefault
或SingleOrDefault
。如果您获得多个结果,则应该只使用Where
。Select
时永远不会创建重复项)。我假设(通过阅读实体名称)特定顾问可能有多个时间表用于同一个项目,但可能这不是真的。
Select
之后,您将遇到重复项目的问题。快速解决方案是使用Distinct
:
return context.Timesheets
.Where(...)
.Select(t => t.Project)
.Distinct()
但我个人认为更好的解决方案是反转查询:从项目开始,在他们的时间表上过滤项目(而不是过滤时间表):
return context.Projects
.Include(p => p.Timesheets)
.Where(p => p.Timesheets.Any(t => t.UserId == userId && ...))
.ToList();
这排除了重复项目的问题。请注意,这还没有解决您的过滤包含"问题
评论中也提到了这一点。这是一个可行的选择,但我发现它是一种肮脏的方法,会在线下创建不直观的代码。
context.Configuration.LazyLoadingEnabled = false;
var parent = context.Set<Entity>().First(e => e.Name = "ABC");
// Load relations in separate query
context.Set<Child>()
.Where(c => c.Parent.Name == "ABC")
.OrderBy(c => c.Name) // You can at least try it but as mentioned above it may not work in all scenarios
.Load();
// Now parent.Children collection should be filled
该示例使用OrderBy
代替Where
,但两者的工作方式相同。
即使您分别查询了孩子和父母,他们的导航属性也会不断更新,因为您在同一个环境中运行查询。
这对你来说是一个可行的选择,但是我对这段代码感到有点担心,因为第二个查询改变第一个查询的结果绝不是可读 。
对我来说,这对于在属性的get
或set
中拥有业务逻辑。它有效,但它会导致意外的行为,并且会使调试变得非常困难。
请注意,您可能很清楚 幕后发生的事情,但是在查看代码时,不同的开发人员很容易掩饰它。
我个人不喜欢这样,但你的意见可能有所不同。
查看您的代码示例,我认为您的数据一致性存在一些问题。您在两个地方使用userId
过滤器:
t => t.UserId == userId
c => c.UserId == userId
如果时间表与顾问相关联,那么这两个实体之间应该存在关系。按照目前的情况,您的项目有一份时间表列表和一份顾问列表,时间表和顾问之间没有明显的关系。
这就是您的查询复杂的原因。你试图嘲笑那里没有的关系。
如果这种关系确实存在,那么查找所有内容会容易得多:
return context.Timesheets
.Include(t => t.Project)
.Include(t => t.Project.Account)
.Include(t => t.Consultant)
.Where(t => t.Consultant.UserId == userId && t.SubjectDate == date && t.WorkingDaySchedules.Count() > 0)
.ToList()
然后你得到了你正在寻找的东西。您不再需要执行两次单独的userId
检查,您不再需要&#34;手动同步&#34;假的关系,查找过程更加简化和可读。
也许你还不知道的事情。你可以重写
t.WorkingDaySchedules.Count() > 0
作为
t.WorkingDaySchedules.Any() //is there at least one item in the collection?
如果您需要,可以添加过滤器的附加好处:
t.WorkingDaySchedules.Any(wds => wds.IsActive) //is there at least one item in the collection that meets the condition?
答案 1 :(得分:0)
有两种方法可以过滤包含实体。
免责声明:我是该项目的所有者Entity Framework Plus
EF + Query IncludeFilter允许轻松过滤包含的实体。
return context.Timesheets.Where(t => t.UserId == userId && t.SubjectDate == date && t.WorkingDaySchedules.Count() > 0)
.Include(t => t.Project)
.Select(t => t.Project)
.IncludeFilter(p => p.Account)
.IncludeFilter(pc => pc.ProjectConsultants.Where(c => c.UserId == userId));
在引擎盖下,图书馆完全是一个投影。
一个限制是全部Include
,但现在即使未指定过滤器(例如帐户),也会使用IncludeFilter
进行调用。