使用EntityFrameworkCore保存数据时触发DbUpdateException异常求助
异常信息
Microsoft.EntityFrameworkCore.DbUpdateException
HResult=0x80131500
Message=保存实体变更时出错,请查看内部异常获取详细信息。
Source=Microsoft.EntityFrameworkCore.Relational
Inner Exception 1:
MySqlException: 主键 '08dad56f-8499-4b4f-81b7-53c3ca5c06eb' 重复输入
问题代码
Account? account=null; using (AppDbContext context = new()) { account = context.Set<Account>() .Include(e=>e.AccountCategory) .First() } if (account == null) return; var inv= new Invoice() { Name=str, Account = account, InvoiceItems = GetInvoiceItems() }; var _context=new AppDbContext(); _context.Set<Invoice>().Add(inv); _context.SaveChanges();
原因分析
你使用了两个独立的DbContext实例:第一个上下文在using块结束后被释放,查询到的Account实体变为脱离跟踪状态。当你将这个脱离跟踪的Account赋值给Invoice.Account,再通过第二个上下文调用Add(inv)时,EF Core会默认将整个对象图(包括Account)标记为Added状态,尝试插入一条新的Account记录,但该Account的主键已存在于数据库中,因此触发主键重复异常。
解决方案
方案1:复用同一个DbContext实例
将查询与保存逻辑放在同一个DbContext的using块内,确保Account始终处于被跟踪状态,EF Core只会插入Invoice,不会重复插入Account:
using (AppDbContext context = new()) { var account = context.Set<Account>() .Include(e => e.AccountCategory) .FirstOrDefault(); if (account == null) return; var inv = new Invoice() { Name = str, Account = account, InvoiceItems = GetInvoiceItems() }; context.Set<Invoice>().Add(inv); context.SaveChanges(); }
方案2:手动附加Account到新上下文并设置状态为Unchanged
若必须使用不同上下文,需将Account附加到新上下文,并明确标记为Unchanged,告知EF Core该实体已存在于数据库:
Account? account = null; using (AppDbContext context = new()) { account = context.Set<Account>() .Include(e => e.AccountCategory) .FirstOrDefault(); } if (account == null) return; var inv = new Invoice() { Name = str, Account = account, InvoiceItems = GetInvoiceItems() }; using (var _context = new AppDbContext()) { _context.Attach(account).State = EntityState.Unchanged; _context.Set<Invoice>().Add(inv); _context.SaveChanges(); }
方案3:直接设置外键属性(推荐)
如果Invoice实体有对应的外键属性(如AccountId),直接设置外键而非整个Account实体,避免EF Core处理对象图时的误操作:
Account? account = null; using (AppDbContext context = new()) { account = context.Set<Account>() .FirstOrDefault(); } if (account == null) return; var inv = new Invoice() { Name = str, AccountId = account.Id, InvoiceItems = GetInvoiceItems() }; using (var _context = new AppDbContext()) { _context.Set<Invoice>().Add(inv); _context.SaveChanges(); }
内容的提问来源于stack exchange,提问作者eng_hr.ye

