mysql 使用EntityFrameworkCore保存时遇到异常

6vl6ewon  于 2023-01-16  发布在  Mysql
关注(0)|答案(1)|浏览(149)

Microsoft.EntityFrameworkCore.DbUpdateException HResult= 0x 80131500消息=保存实体更改时出错。有关详细信息,请参阅内部异常。源=Microsoft.EntityFrameworkCore.Relational
内部异常1:异常:关键字“主要”的重复条目“08 dad 56 f-8499- 4 b4 f-81 b7 - 53 c3 ca 5c 06 eb”

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();
xwbd5t1u

xwbd5t1u1#

EF DbContexts跟踪对象的引用,这使它们可以在查看实体时确定实体是否已更新或是否需要插入某些内容。EF并不跟踪数据库中的所有内容来进行这些确定,因此您必须遵循一些基本规则以确保它按您预期的方式工作。
您的问题是因为您使用了2个单独的DbContext示例来执行更新。1个示例用于读取帐户,另一个示例用于保存新发票。但是,发票引用了您从第一个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。这包括调用来做一些事情,比如获取发票项,所以如果你是using范围内的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正在跟踪Account,因此我们可以创建发票,通过将其添加到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跟踪,或者您将其附加到的上下文已经跟踪同一记录的另一个示例,则附加实体可能会导致引发其他异常。它在这里可能有效,但当您在其他区域尝试时会失败,或者更糟。在运行时的某些场景中可以工作,但由于该特定场景中涉及的引用而失败。

相关问题