EF6中如何在Include查询中获取关联项目的最新版本
解决EF6中获取站点及对应最新版本项目的N+1查询问题
问题背景
现有两张数据表:
- Site表:
| ID | Name |
|---|---|
| 1 | site1 |
| 2 | Site2 |
- Project表:
ID与Version为联合主键
| ID | Version | ProjectName | SiteId |
|---|---|---|---|
| 1 | 1 | Test1 | 1 |
| 1 | 2 | Test2 | 1 |
需求是获取所有站点,以及每个站点对应的最新版本项目。原方案通过循环站点逐一查询项目,导致多次数据库往返、性能低下,且尝试的分组降序写法EF6无法转换为SQL语句。
优化方案
方案1:子查询筛选最新版本(EF6原生兼容)
先通过子查询获取每个Project.ID对应的最大版本号,再关联查询完整项目数据,最后与Site表关联,仅需一次数据库往返:
// 子查询:获取每个项目ID的最大版本 var latestProjectVersions = context.Projects .GroupBy(p => p.ID) .Select(g => new { ProjectId = g.Key, MaxVersion = g.Max(p => p.Version) }); // 关联站点与对应最新版本项目 var sitesWithLatestProjects = await context.Sites .AsNoTracking() .Include(s => s.UserRelations) .Select(s => new { Site = s, LatestProjects = context.Projects .AsNoTracking() .Join(latestProjectVersions, p => new { p.ID, p.Version }, lp => new { ProjectId = lp.ProjectId, MaxVersion = lp.MaxVersion }, (p, lp) => p) .Where(p => p.SiteId == s.ID) .ToList() }) .ToListAsync(); // 映射回原实体结构 var result = sitesWithLatestProjects.Select(item => { item.Site.Projects = item.LatestProjects; return item.Site; }).ToList(); return result;
方案2:窗口函数实现(需数据库支持)
如果你的数据库支持窗口函数(如SQL Server 2008+),可以用ROW_NUMBER()为每个项目的版本排序,直接取序号为1的最新版本,查询效率更高:
// 筛选每个项目的最新版本 var latestProjects = context.Projects .AsNoTracking() .Select(p => new { p.ID, p.Version, p.ProjectName, p.SiteId, RowNum = SqlFunctions.RowNumber() .Over(PartitionBy: p.ID, OrderBy: "Version DESC") }) .Where(p => p.RowNum == 1); // 关联站点与最新项目 var sitesWithLatestProjects = await context.Sites .AsNoTracking() .Include(s => s.UserRelations) .Select(s => new { Site = s, LatestProjects = latestProjects.Where(p => p.SiteId == s.ID) .Select(p => new Project { ID = p.ID, Version = p.Version, ProjectName = p.ProjectName, SiteId = p.SiteId }) .ToList() }) .ToListAsync(); // 映射回原实体 var result = sitesWithLatestProjects.Select(item => { item.Site.Projects = item.LatestProjects; return item.Site; }).ToList(); return result;
说明
- 方案1基于EF6原生Linq支持,无需依赖数据库特定函数,兼容性更强;
- 方案2利用数据库窗口函数,查询性能更优,但需要数据库支持
ROW_NUMBER(); - 两种方案均仅需一次数据库查询,彻底解决原方案的N+1性能问题;
- 若
Site与Project已配置好导航属性(Site包含ICollection<Project> Projects,Project包含Site Site),可简化关联逻辑。
内容的提问来源于stack exchange,提问作者Kristian Schneider
相关产品推荐
相关产品推荐

