VS2022升级v17.2.2后EF Core插入Member误写Teams表报NULL错误
升级Visual Studio 2022至v17.2.2版本后,ASP.NET应用突发SQL数据库插入错误,提示非空字段不允许空值无法完成插入操作。升级前从未出现该问题,甚至此前开发完成的同框架ASP.NET应用也出现了同类报错。
实际业务逻辑仅需要向Members表插入带TeamId的成员记录,完全不需要向Teams表写入任何数据,但抛出的核心错误为:无法向Tjoba-Server.dbo.Teams表的Class列插入NULL值,该列不允许为空,插入操作失败,语句已终止。
实体模型定义
public class Member { public int MemberId { get; set; } [Required(ErrorMessage = "Dit is een verplicht veld")] public string FirstName { get; set; } [Required(ErrorMessage = "Dit is een verplicht veld")] public string LastName { get; set; } public string MemberImage { get; set; } public Gender Gender { get; set; } public int TeamId { get; set; } public virtual Team Team { get; set; } = new(); }
定义最后一个virtual Team导航属性的目的,是查询Member时通过预加载关联拉取对应Team的关联数据。
关联查询逻辑
public async Task<IEnumerable<Member>> GetAllMembersByTeamId(int TeamId) { var result = await dbContext.Members .Include(t => t.Team) .Where(t => t.TeamId == TeamId) .ToListAsync(); if (result != null) { return result; } else return null; }
成员写入逻辑
public class MemberRepository : IMemberRepository { private readonly AppDbContext dbContext; public MemberRepository(AppDbContext dbContext) { this.dbContext = dbContext; } public async Task<Member> AddMember(Member obj) { await dbContext.Members.AddAsync(obj); await dbContext.SaveChangesAsync(); return obj; } }
页面提交逻辑
页面初始化时先查询待关联的团队信息:
protected async override Task OnInitializedAsync() { Team = await TeamsRepository.GetTeamById(int.Parse(Id)); }
通过EditForm的OnValidSubmit事件触发提交逻辑:
public async Task HandleValidSubmit() { ConfirmationTitle = StaticString.ConfirmationTitle; ConfirmationMessage = StaticString.ConfirmationMessage; Member.TeamId = int.Parse(Id); await MemberRepository.AddMember(Member); NavigationManager.NavigateTo("/adminpage"); }
后端核心异常
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Class', table 'Tjoba-Server.dbo.Teams'; column does not allow nulls. INSERT fails. The statement has been terminated.
前端捕获异常
blazor.server.js:1 [2022-06-02T13:13:22.000Z] Error: Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Class', table 'Tjoba-Server.dbo.Teams'; column does not allow nulls. INSERT fails. The statement has been terminated. The statement has been terminated. ClientConnectionId:2df86c5d-9cee-4c70-ad12-ebb86a036fe9 Error Number:515,State:2,Class:16
Uncaught (in promise) Error: Cannot send data if the connection is not in the 'Connected' State.
Member实体中给Team导航属性设置了默认初始化= new(),提交新增Member时,若Member实例携带的Team对象不是被当前DbContext跟踪的已存在实体,EF Core会将这个仅初始化了空对象、缺少Class等必填字段值的Team识别为待新增实体,执行SaveChangesAsync时会自动尝试向Teams表插入这条不完整的记录,最终触发非空约束报错。
升级VS2022到17.2.2时同步更新了内置的EF Core版本,新版本调整了关联实体的跟踪判定逻辑,因此之前可以正常运行的代码升级后批量出现同类问题。
手动标记关联实体状态的方案可以生效,代码如下:
public async Task<Member> AddMember(Member member) { if (member.Team != null) { dbContext.Entry(member.Team).State = EntityState.Unchanged; } var result = await dbContext.Members.AddAsync(member); await dbContext.SaveChangesAsync(); return result.Entity; }
更推荐的长期修复方式是:新增Member提交时,不要给导航属性绑定空的Team实例,仅给TeamId外键赋关联值即可,从根源上避免EF Core误将空导航属性识别为待新增实体。
内容的提问来源于stack exchange,提问作者Gerald

