EF Core 8如何将原生SQL查询映射到含复杂类型的非实体类?
EF Core 8原生SQL查询映射复杂嵌套类型的实用方案
针对你提到的场景——用原生SQL查询结果映射到包含嵌套复杂类型的对象(类A包含类B属性),EF Core 8确实有不用低级数据读取器的简洁方案,下面两种方法都能解决问题:
方法1:通过中间DTO做投影转换
这是最通用的方案,不管B是不是EF映射的类型都能用。步骤很简单:
- 先定义一个平结构的中间DTO,把SQL返回的所有字段都包含进去:
public class AFlatDto { public int Id { get; set; } public int IdB { get; set; } public string Description { get; set; } }
- 用
SqlQuery查询这个DTO,再通过LINQ的Select手动组装成嵌套的A和B对象:
var sql = @"SELECT a.Id, b.IdB, b.Description FROM TableA a JOIN TableB b ON a.BId = b.IdB WHERE a.Id = @Id"; var queryResult = context.Database.SqlQuery<AFlatDto>(sql, new SqlParameter("@Id", 1)); var finalResult = queryResult.Select(dto => new A { Id = dto.Id, B = new B { IdB = dto.IdB, Description = dto.Description } }).ToList();
为什么不用SqlQueryRaw<dynamic>?因为dynamic类型无法自动识别嵌套属性的映射规则,强类型的DTO能让EF准确映射SQL字段,再手动转换到目标类型就很稳妥。
方法2:配置复杂类型直接映射
如果B可以被定义为EF的值对象/复杂类型(没有独立主键,完全依赖于A存在),可以直接让EF自动映射嵌套属性:
- 在DbContext的
OnModelCreating里配置A的B属性为复杂类型:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<A>().OwnsOne(a => a.B); }
- 调整SQL返回的字段名,和EF默认生成的复杂类型列名匹配(格式为
[嵌套属性名_子属性名]),然后直接查询A类型:
var sql = @"SELECT a.Id, b.IdB AS [B_IdB], b.Description AS [B_Description] FROM TableA a JOIN TableB b ON a.BId = b.IdB WHERE a.Id = @Id"; var result = context.Database.SqlQuery<A>(sql, new SqlParameter("@Id", 1)).ToList();
这样EF会自动把B_IdB和B_Description映射到A的B属性对应的子字段里,不需要手动转换。
注意:优先用SqlQuery而不是SqlQueryRaw,前者支持参数化查询,能避免SQL注入风险。
内容的提问来源于stack exchange,提问作者Luis Abreu
相关产品推荐
相关产品推荐

