在调用 SaveChangesAsync() 时在一对多关系中添加子项会导致异常

Adding child within One-to-Many relationship results in exception when SaveChangesAsync() is called

首先,我尝试在 SO 和其他地方找到我的问题的解决方案,但其中 none 可以解决我的问题。我从生产代码中制作了我的问题的简化版本,以使其更易于理解 我有一个名为 WorkEntity 的父实体。它与名为 QuotationEntity 的实体具有一对一的关系。此 QuotationEntityQuotationLine. 具有一对多关系 到目前为止一切顺利。让我们看看 classes 长什么样。首先是 WorkEntity:

public class WorkEntity : Entity, IAggregateRoot
{
    public string Name { get; set; }

    public QuotationEntity QuotationEntity { get; set; }


    public WorkEntity()
    {
        QuotationEntity = new QuotationEntity();
    }
}

然后QuotationEntity

public class QuotationEntity : Entity
{
    public string Name { get; set; }

    public Guid WorkEntityId { get; set; }
    public WorkEntity WorkEntity { get; set; }

    public ICollection<QuotationLine> Lines { get; set; } = new List<QuotationLine>();
}

最后但并非最不重要的 QuotationLine:

public class QuotationLine : Entity
{
    public string Price { get; set; }

    public Guid QuotationEntityId { get; set; }
    public QuotationEntity QuotationEntity { get; set; }
}

让我们看看我的测试代码是什么样子的,以及我遇到的异常是什么:

    [Fact]
    public async Task TestAddingChildElement()
    {
        var repo = new WorkEntityRepository(DbContext);
        var workEntity = new WorkEntity();
        repo.Insert(workEntity);

        await DbContext.SaveChangesAsync();

        var workFromDb = await repo.GetAsync(workEntity.Id);
        workFromDb.QuotationEntity.Lines.Add(new QuotationLine());

        await DbContext.SaveChangesAsync();
    }

我的测试相当简单。首先,我创建了 WorkEntity 及其对应的 QuotationEntity 而没有 QuotationLine。持久化这个实体后,我用相应的存储库检索它,然后我尝试将 QuotationLine 添加到 QuotationEntity。第二次调用 DbContext.SaveChangesAsync() 时,出现以下异常:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: 数据库操作预期影响 1 行但实际上影响了 0 行。自加载实体以来,数据可能已被修改或删除。有关理解和处理乐观并发异常的信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=527962

WorkRepository class 看起来像这样:

public class WorkEntityRepository : IWorkEntityRepository
{
    private readonly DbSet<WorkEntity> _entities;
    private readonly WarehousingDbContext _dbContext;

    public WorkEntityRepository(WarehousingDbContext dbContext)
    {
        if (dbContext == null)
        {
            throw new ArgumentNullException(nameof(dbContext));
        }

        _dbContext = dbContext;
        _entities = dbContext.Set<WorkEntity>();
    }

    public Task<WorkEntity> GetAsync(Guid id)
    {
        return GetWorkEntityIncludingChilds().SingleAsync(p => p.Id == id);
    }


    public WorkEntity Insert(WorkEntity workEntity)
    {

        var entity = _entities.Add(workEntity);

        return entity.Entity;
    }


    private IQueryable<WorkEntity> GetWorkEntityIncludingChilds()
    {
        return _entities
            .Include(c => c.QuotationEntity)
                .ThenInclude(w => w.Lines);
    }
}

为了配置 WorkEntityQuotationEntity 之间的一对一关系,我使用以下代码片段:

        builder.HasOne(a => a.QuotationEntity)
            .WithOne(b => b.WorkEntity)
            .HasForeignKey<QuotationEntity>(b => b.WorkEntityId);

最后但同样重要的是,我在内存中使用 SQLite 数据库和 EF Core 3.1 来重现此问题:

    private void SetUpInMemorySqlLiteDb(ServiceCollection services)
    {
        services.AddDbContext<WarehousingDbContext>(o => o
            .UseSqlite("Data Source=:memory:")
            .EnableSensitiveDataLogging());
    }

由于我花了很多时间试图解决这个问题,所以我有一些发现:

  1. 此异常的一个可能原因可能是从存储库检索 WorkEntity 时未包含子实体。如果我不包括它们,那么实体不被 EF ChangeTracker 跟踪是合乎逻辑的。但如您所见,我包括了所有子实体。

  2. 我在调试过程中发现 QuotationLine 没有得到正确的状态。如果我将其状态明确设置为:

        var quotationLine = new QuotationLine();
        DbContext.Entry(quotationLine).State = EntityState.Added;
        workFromDb.QuotationEntity.Lines.Add(quotationLine);
    

然后一切都按预期工作。但我认为这不是解决方案,它只是一个 hacky 解决方法。据我所知,EFCore 应该处理元素的 ChangeTracking。

让我知道,如果需要,我可以为我的问题提供更多代码。在此先感谢您的帮助!

But I think it is not the solution, it is just a hacky workaround. The EFCore should handle the ChangeTracking of the element as far as I know.

我觉得你应该改变主意了!

我对这个问题做了一些研究。因此,我能说的是,它可能是 EF Core 本身或其 sqlite 提供程序中的一些已知错误。查看这些票以了解更多信息:

https://github.com/dotnet/efcore/issues/9166

https://github.com/dotnet/efcore/issues/9803#issuecomment-391315406