使用EntityFrameworkCore保存时遇到异常



Microsoft.EntityFrameworkCore.DbUpdateExceptionHResult=0x80131500消息=保存实体更改时出错。有关详细信息,请参阅内部异常。Source=Microsoft.EntityFrameworkCore.Relational

内部异常1:MySqlException:密钥"PRIMARY"的条目"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();

EF DbContexts用于跟踪对象的引用,这使他们在查看实体时可以确定实体是否已更新或可能需要插入的内容。EF不会跟踪数据库中的所有内容来做出这些决定,所以你必须遵循一些基本规则来确保它按你期望的方式工作。

您的问题是因为您正在使用两个单独的DbContext实例来执行更新。1个用于读取帐户,1个用于保存新发票。然而,invoice引用了您从第一个DbContext中读取的帐户,而这个新的DbContext不知道它反映了现有的帐户记录。

作为一般规则,您应该在单个DbContext的范围内工作,并且对实体的引用最好不要离开DbContext的作用域。无论如何,它们都可以,但它们实际上是独立的实体,如果你要引用它们,它们需要特殊处理。

单一数据库上下文:

using (AppDbContext context = new())
{
account = context.Set<Account>()
.Include(e=>e.AccountCategory)
.Single(x => x.AccountId == accountId);

if (account == null) return;
var inv = new Invoice()
{
Name=str,
Account = account,
InvoiceItems = GetInvoiceItems(context)
};
context.Set<Invoice>().Add(inv);
context.SaveChanges();
}

这里有重要的细节。所有操作都应使用单个DbContext。这包括调用以执行诸如获取发票项之类的操作,因此,如果您是usingscope您的DbContext,则传递DbContext实例,以便它跟踪您加载的所有实体,而不是使用一堆单独的DbContext来加载实体。

更好的是,使用导航属性,其中一个帐户有一组与其关联的发票,这可以简化:

using (AppDbContext context = new())
{
account = context.Set<Account>()
.Include(e => e.Invoices)
.Single(x => x.AccountId == accountId);
if (account == null) return;
var inv = new Invoice()
{
Name=str,
InvoiceItems = GetInvoiceItems(context)
};
account.Invoices.Add(inv);
context.SaveChanges();
}

由于DbContext正在跟踪帐户,我们可以创建发票,通过添加到Invoices集合将其关联到帐户,EF会自动计算出所需的所有FK关联和引用。

按照您所做的方式,您将需要将任何和所有引用关联到将保存新实体的DbContext。如果DbContext作用域由于任何原因没有结束,或者读取实体没有首先分离,这可能会变得混乱。使用您所拥有的代码,并假设GetInvoiceItems方法正在确定其自身DbContext的范围,以下内容应该有效:

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.Attach(account);
_context.Attach(account.AccountCategory);
foreach(var invoiceItem in inv.InvoiceItems)
_context.Attach(invoiceItem);
_context.Set<Invoice>().Add(inv);
_context.SaveChanges();

在这里,我们将帐户和发票项附加到要保存发票的DbContext实例。如果实体仍由另一个DbContext跟踪,或者您将其附加到的上下文已在跟踪同一记录的另一个实例,则附加实体可能会引发其他异常。它可能在这里工作,但当你在其他领域尝试时会失败,或者更糟的是,它在运行时在某些场景中工作但失败,只是因为该特定场景中涉及的引用。

相关内容

最新更新