EF Core+OData API中外键为空抛出SqlNullValueException求助
SqlNullValueException 问题解决 问题现象
在PersonView与StaffView实体间配置一对一关联后,当数据库Person.Persons表的Staff_ID字段为Null时,EF Core会抛出SqlNullValueException: Data is Null异常。
实体定义
PersonView(映射Person.Persons表)
[Key] public int PersonId { get; set; } public int? StaffId { get; set; } public virtual StaffView? StaffView { get; set; }
StaffView(映射Person.Staff表)
[Key] public int StaffId { get; set; } public int? PersonId { get; set; } public virtual PersonView? PersonView { get; set; }
DbContext配置
public virtual DbSet<PersonView>? PersonViews { get; set; } = null!; public virtual DbSet<StaffView> StaffViews { get; set; } = null!; modelBuilder.Entity<PersonView>(entity => { entity.ToTable("Persons", "Person"); entity.Property(e => e.PersonId).HasColumnName("Person_ID").ValueGeneratedOnAdd(); entity.HasKey(e => e.PersonId); entity.Property(e => e.StaffId).HasColumnName("Staff_ID"); entity.HasOne(a => a.StaffView) .WithOne(b => b.PersonView) .HasPrincipalKey<PersonView>(b => b.StaffId) .HasForeignKey<StaffView>(b => b.StaffId) .IsRequired(false); }); modelBuilder.Entity<StaffView>(entity => { entity.ToTable("Staff", "Person"); entity.Property(e => e.StaffId).HasColumnName("Staff_ID").ValueGeneratedOnAdd(); entity.HasKey(e => e.StaffId); entity.Property(e => e.PersonId).HasColumnName("Person_ID"); });
EDM配置(Program.cs)
builder.EntitySet<PersonView>("OViewPersons");
控制器代码
public class OViewPersonsController : ODataController { private readonly ViewContext? _context; public OViewPersonsController(ViewContext context) { _context = context; } [HttpGet] [EnableQuery(MaxExpansionDepth = 6)] public ActionResult<IQueryable<PersonView>> Get() { try { IQueryable<PersonView> queryResults = _context.PersonViews .Include(sta => sta.StaffView); return Ok(queryResults); } catch (Exception e) { return StatusCode(StatusCodes.Status500InternalServerError, e.Message); } } }
数据库架构
[Person].[Persons] [Person_ID] [int] IDENTITY(1,1) NOT NULL [Staff_ID] [int] NULL [Person].[Staff] [Staff_ID] [int] IDENTITY(1,1) NOT NULL [Person_ID] [int] NULL
触发异常的场景
当Persons表存在Staff_ID为Null的记录时:
Person_Id Staff_Id -------------------- 397748 NULL
抛出异常信息:
Microsoft.EntityFrameworkCore.Query: Error: An exception occurred while iterating over the results of a query for context type 'GatewayApi.DbContexts.GatewayApiContext'.
System.Data.SqlTypes.SqlNullValueException: Data is Null. This method or property cannot be called on Null values.at Microsoft.Data.SqlClient.SqlBuffer.get_Int32()
at lambda_method321(Closure , QueryContext , DbDataReader , ResultContext , SingleQueryResultCoordinator )
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.Enumerator.MoveNext()
环境版本
- VS 2022 - Version 17.3.6
- Microsoft.EntityFrameworkCore {6.0.10}
- Microsoft.EntityFrameworkCore.SqlServer {6.0.10}
- Microsoft.AspNetCore.OData {8.0.11}
- 其他依赖:Swashbuckle.AspNetCore 6.4.0、Microsoft.AspNetCore.Mvc.NewtonsoftJson 6.0.10等
已尝试无效方案
- 在模型类中添加
ForeignKey注解 - 调整DbContext中
.IsRequired(false)配置 - 修改参数可空性设置
- 项目生成配置中禁用Nullable
- 将
Persons表的Staff_ID改为非可空
解决方案
问题根源是一对一关联的EF Core配置逻辑错误:当前配置将PersonView.StaffId设为主体键,同时把StaffView.StaffId(自身主键)设为外键,导致EF Core读取Null值时无法完成类型转换,触发异常。
修正关联配置
重新配置实体关联,明确外键指向关系:
基础配置(仅支持从PersonView导航到StaffView)
修改DbContext中PersonView的关联代码:
modelBuilder.Entity<PersonView>(entity => { // 保留原有其他配置... // 修正一对一关联:PersonView.StaffId是外键,指向StaffView的主键StaffId entity.HasOne(p => p.StaffView) .WithOne(s => s.PersonView) .HasForeignKey<PersonView>(p => p.StaffId) .IsRequired(false); });
双向关联配置(支持双向导航)
如果需要从StaffView也能导航到PersonView,补充StaffView的关联配置:
modelBuilder.Entity<StaffView>(entity => { // 保留原有其他配置... entity.HasOne(s => s.PersonView) .WithOne(p => p.StaffView) .HasForeignKey<StaffView>(s => s.PersonId) .IsRequired(false); });
验证效果
修正配置后重新运行查询,当Persons.Staff_ID为Null时,PersonView.StaffView会被赋值为Null,不再抛出SqlNullValueException。
额外注意事项
如果数据库中Persons.Staff_ID存在重复的非Null值,可能引发一对一关联的唯一性冲突,此时需给Persons.Staff_ID添加唯一约束,或根据业务场景调整为一对多关联。
内容的提问来源于stack exchange,提问作者Edster

