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

EF Core+OData API中外键为空抛出SqlNullValueException求助

EF Core 外键为空时抛出 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:55:20