You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core中EF FromSql查询返回Speciality导航属性为null的问题

ASP.NET Core EF Core:FromSql查询后导航属性为null的问题

问题详情

使用FromSql执行关联查询时,返回的Doctor实体中speciality导航属性始终为null,但相同SQL在SQL Server中能查到正确的SpecialityName。

控制器代码

public IActionResult Index() 
{
    var list = _context.Doctors
                       .FromSql($"select Id, FirstName, LastName,Doctors.SpecialityId , Email , SpecialityName from Doctors join Specialities on Doctors.SpecialityId = Specialities.SpecialityId")
                       .ToList(); 
    return Json(list); 
} 

返回结果

{
    "id": 3,
    "firstName": "Hatem",
    "lastName": "Aiman",
    "email": "hatem@gmail.com",
    "specialityId": 1,
    "speciality": null
}

模型与DbContext定义

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
    {
    }

    public DbSet<Doctor> Doctors { get; set; }
    public DbSet<Speciality> Specialities { get; set; }  
}

public class Doctor
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }

    public int SpecialityId { get; set; }
    public Speciality speciality { get; set; }
}

public class Speciality
{
    public int SpecialityId { get; set; }
    public string SpecialityName { get; set; }

    public ICollection<Doctor> Doctor { get; set; }
}

问题原因

FromSql仅负责将查询结果映射到Doctor实体的标量属性(如Id、SpecialityId),EF Core不会自动填充导航属性,也不会把查询中的SpecialityName字段映射到speciality对象里。导航属性的加载需要显式通过Include或LINQ关联查询处理。


解决方案

方案1:使用DTO投影(推荐)

创建包含所需字段的DTO类,直接从SQL查询投影结果,避免依赖导航属性:

// 定义DTO
public class DoctorSpecialityDto
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public int SpecialityId { get; set; }
    public string SpecialityName { get; set; }
}

// 控制器查询
public IActionResult Index() 
{
    var sql = @"SELECT Id, FirstName, LastName, Doctors.SpecialityId, Email, SpecialityName
                FROM Doctors 
                JOIN Specialities ON Doctors.SpecialityId = Specialities.SpecialityId";
    
    var list = _context.Set<DoctorSpecialityDto>()
                       .FromSqlRaw(sql)
                       .ToList(); 
    return Json(list); 
}

方案2:用EF LINQ查询+Include加载导航属性

放弃手写SQL,改用EF Core的LINQ查询,通过Include显式加载关联的speciality:

public IActionResult Index() 
{
    var list = _context.Doctors
                       .Include(d => d.speciality) // 注意属性名与模型一致(小写speciality)
                       .ToList(); 
    return Json(list); 
}

如果需要过滤数据,直接在查询中添加Where条件即可,EF会自动生成对应的关联SQL。

方案3:手动填充导航属性(不推荐)

若必须使用手写SQL,可查询后手动获取关联数据并填充:

public IActionResult Index() 
{
    // 查询医生基础数据
    var doctors = _context.Doctors
                       .FromSql($"SELECT Id, FirstName, LastName, Doctors.SpecialityId, Email FROM Doctors")
                       .ToList(); 
    
    // 批量获取关联的科室数据
    var specialityIds = doctors.Select(d => d.SpecialityId).Distinct().ToList();
    var specialities = _context.Specialities
                               .Where(s => specialityIds.Contains(s.SpecialityId))
                               .ToList();
    
    // 手动填充导航属性
    foreach (var doctor in doctors)
    {
        doctor.speciality = specialities.FirstOrDefault(s => s.SpecialityId == doctor.SpecialityId);
    }
    
    return Json(doctors); 
}

额外提示

  • 模型中speciality属性为小写,JSON返回字段会与之保持一致;若需规范命名,可添加[JsonPropertyName("speciality")]特性或启用EF的约定命名规则。
  • FromSqlRaw比FromSql更适合处理原生SQL字符串,若有参数请使用参数化查询避免注入风险。

内容的提问来源于stack exchange,提问作者Hatem Aiman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 03:53:13