EF 7中基于子表关联条件构建查询过滤表达式的指导
在EF 7中实现复杂子表关联过滤的方案
针对你迁移到EF 7后遇到的非键字段关联子表过滤场景,结合LinqKit的PredicateBuilder,可以通过以下方式实现需求:
核心过滤逻辑实现
首先修正仓储代码中的Include错误(StudentTestTaken应为StudentTestHistory),然后添加针对StudentTestAvailability与StudentTestHistory关联匹配的过滤条件:
public class ReportRepo { public async Task<List<Person>> BuildReport(Filter filter) { var predicate = PredicateBuilder.New<Person>(true); // 原有简单过滤条件 if (!string.IsNullOrWhiteSpace(filter.AltId)) { predicate = predicate.And(a => a.AltId == filter.AltId); } // 添加其他已有过滤条件... // 核心:关联StudentTestAvailability与StudentTestHistory的过滤逻辑 predicate = predicate.And(p => // 确保当前Person关联Student(对应原SQL的INNER JOIN Student) p.Student != null // 存在匹配的StudentTestHistory记录 && p.Student.StudentTestHistory.Any(sth => // 该记录存在对应的StudentTestAvailability匹配项 p.Student.StudentTestAvailability.Any(sta => sth.DateTaken == sta.DateAvailable && sth.TestNameId == sta.TestNameId && sth.LocationId == sta.LocationId // TestNameId参数过滤:参数<=0时忽略,否则匹配 && (filter.TestNameId <= 0 || sta.TestNameId == filter.TestNameId) // LocationId参数过滤:参数<=0时忽略,否则匹配 && (filter.LocationId <= 0 || sta.LocationId == filter.LocationId) ) ) ); using var context = _factory.CreateDbContext(); { return await context.Person .Include(i => i.Employee) .Include(i => i.Student) .ThenInclude(t => t.StudentTestAvailability) .Include(i => i.Student) .ThenInclude(t => t.StudentTestHistory) // 修正原代码中的名称错误 .AsExpandable() .Where(predicate) .ToListAsync(); } } }
逻辑说明
- 关联匹配:通过嵌套的
Any方法模拟原SQL中两个子表的关联逻辑,确保存在一对StudentTestHistory和StudentTestAvailability记录满足日期、测试名称ID、地点ID完全匹配。 - 参数动态过滤:通过
(filter.TestNameId <=0 || ...)的条件实现“参数大于0时才匹配”的逻辑,等价于原SQL中的@nameId<1 OR sta.TestNameId=@nameId。 - Student关联约束:添加
p.Student != null确保只保留有Student关联的Person,对应原SQL的INNER JOIN Student。
性能优化提示
- 如果数据量较大,建议为
StudentTestAvailability的DateAvailable、TestNameId、LocationId字段建立联合索引,为StudentTestHistory的DateTaken、TestNameId、LocationId字段建立联合索引,提升查询效率。 - 若不需要返回所有关联子表数据,可以移除不必要的
Include,仅保留查询所需字段,进一步优化性能。
内容的提问来源于stack exchange,提问作者Albert Brennan
相关产品推荐
相关产品推荐

