EF Core查询实体时如何携带Level元数据并保留查询组合能力
解决方案
方案一:使用EF Core阴影属性(推荐)
阴影属性允许你在实体类中不定义属性,却能在EF Core中映射查询结果的字段,完美适配你的场景:
- 配置阴影属性
在你的DbContext的OnModelCreating方法中,为MyEntity添加Level阴影属性:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 为MyEntity添加int类型的阴影属性Level modelBuilder.Entity<MyEntity>().Property<int>("Level"); }
- 编写CTE查询并映射阴影属性
执行CTE查询时,确保结果包含实体原有字段和Level列,EF Core会自动将Level值映射到阴影属性中,同时不影响Include的使用:
var cteQuery = @"WITH EntityHierarchy AS ( SELECT Id, Name, ParentId, 0 AS Level FROM MyEntities WHERE Name LIKE '%Bob%' UNION ALL SELECT e.Id, e.Name, e.ParentId, eh.Level + 1 FROM MyEntities e INNER JOIN EntityHierarchy eh ON e.Id = eh.ParentId ) SELECT Id, Name, ParentId, Level FROM EntityHierarchy"; var entitiesWithLevel = await context.MyEntities .FromSqlRaw(cteQuery) .Include(e => e.OtherData) // 正常加载关联数据 .ToListAsync();
- 读取阴影属性的值
通过EF.Property方法获取每个实体的Level值:
foreach (var entity in entitiesWithLevel) { int level = EF.Property<int>(entity, "Level"); // 后续业务处理 }
该方案优势:
- 无需修改
MyEntity实体类,不破坏原有结构 - 支持所有EF Core查询特性(如
Include、ThenInclude) - 单次数据库查询,性能高效
方案二:分两次查询并关联结果
如果不想修改DbContext配置,可以分两步实现:
- 获取ID与Level的映射关系
var levelMappings = await context.Database.SqlQuery<(int Id, int Level)>(@" WITH EntityHierarchy AS ( SELECT Id, 0 AS Level FROM MyEntities WHERE Name LIKE '%Bob%' UNION ALL SELECT e.Id, eh.Level + 1 FROM MyEntities e INNER JOIN EntityHierarchy eh ON e.Id = eh.ParentId ) SELECT Id, Level FROM EntityHierarchy") .ToListAsync();
- 查询实体并加载关联数据
var targetIds = levelMappings.Select(m => m.Id).ToList(); var entities = await context.MyEntities .Where(e => targetIds.Contains(e.Id)) .Include(e => e.OtherData) .ToListAsync();
- 关联实体与Level值
var result = entities.Join( levelMappings, entity => entity.Id, mapping => mapping.Id, (entity, mapping) => new { Entity = entity, mapping.Level } ).ToList();
该方案劣势是需要两次数据库查询,但无需修改现有DbContext配置。
内容的提问来源于stack exchange,提问作者Anders Forsgren
相关产品推荐
相关产品推荐

