Entity framework EF中的过滤器包括
我有一个LINQ查询,它在Include中给出了一个过滤器错误。 在搜索我的朋友Google时,我发现无法在包含中进行过滤。我已经找到了一些方法来做这件事,但我不能让它在我的具体情况下工作Entity framework EF中的过滤器包括,entity-framework,linq,Entity Framework,Linq,我有一个LINQ查询,它在Include中给出了一个过滤器错误。 在搜索我的朋友Google时,我发现无法在包含中进行过滤。我已经找到了一些方法来做这件事,但我不能让它在我的具体情况下工作 return context.Timesheets.Where(t => t.UserId == userId && t.SubjectDate == date && t.WorkingDaySchedules.Count() > 0) .Include(t
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));
这是最后一个让我头疼的内容:)
有人知道怎么做吗?我认为有几点需要改进
包含不用于过滤。 这根本不是这样做的正确地点
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
用于自动(隐式)加载相关数据。但是,由于您在选择中明确说明了所需的数据,因此不再需要隐式包含。EF会给你你想要的
您的选择不直观
我想你期待的和你得到的不一样。看看代码:
return context.Timesheets //1
.Where(...) //2
.Select(t => t.Project) //3
看看发生了什么:
您可以选择所有时间表
您过滤了时间表,并留下了时间表的子集
您将获得每个时间表的项目列表
如果筛选(步骤2)给您留下了来自同一项目的多个时间表,那么。选择(t=>t.project)
将为您提供同一项目的多个实例。这可不好
这里有两个例外:
- 你知道你总共会找到一张时间表。但是您应该先使用
First
,Single
,FirstOrDefault
或SingleOrDefault
。只有在可能得到多个结果的情况下,才应使用Where
- 您希望有多个时间表,但您知道在同一个项目中永远找不到两个时间表(因此在调用
选择时永远不会创建重复的时间表)。我会假设(通过阅读实体名称),一个特定的顾问可能会为同一个项目提供多个时间表,但可能不是这样。
- 如果我的推断是正确的,那么在执行
选择后,您将遇到重复项目的问题
- 如果我的推断不正确,那么我希望时间表和顾问之间的关系会更密切,因为每个项目顾问都有一个(或没有)时间表,而不超过一个。但您当前的数据结构在时间表和顾问之间缺乏任何真正的关系
一个快速的解决方案是使用Distinct
:
return context.Timesheets
.Where(...)
.Select(t => t.Project)
.Distinct()
但我个人认为,一个更好的解决方案是反转查找:从项目开始,过滤其时间表上的项目(而不是过滤时间表):
这就排除了重复项目的问题。请注意,这还不能解决“过滤包含”问题
同一上下文中的独立查询
评论中也提到了这一点。这是一个可行的选择,但我发现这是一个肮脏的方法,它将创建不直观的代码
:
然后你就能得到你想要的。您不再需要进行两次单独的userId
检查,也不再需要“手动同步”假关系,查找过程更加简化,可读性更强
次要评论
也许有些事你还不知道。你可以重写
t.WorkingDaySchedules.Count() > 0
作为
此外,如果需要,您还可以添加过滤器:
t.WorkingDaySchedules.Any(wds => wds.IsActive) //is there at least one item in the collection that meets the condition?
有两种方法可以过滤包含实体
- 使用投影(参见@Flater答案)
- 使用第三方库
免责声明:我是项目的所有者
EF+查询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));
在引擎盖下,库正好做了一个投影
一个限制是allInclude
,但现在可以使用IncludeFilter
调用,即使没有指定诸如帐户之类的筛选器
Wiki:您可以在单独的查询中加载数据,上下文将连接数据。另外,我不认为包含项目比选择包含项目更重要。这是一个更常见的问题。
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?
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));