代码之家  ›  专栏  ›  技术社区  ›  LemonPotion

SaveChangesAsync期间EF Core中的DbUpdateConcurrenty异常

  •  1
  • LemonPotion  · 技术社区  · 5 月前

    我使用Entity Framework Core时遇到以下错误:

    Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException
    Attempted to update or delete an entity that does not exist in the store.
       at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryTable`1.Update(IUpdateEntry entry, IDiagnosticsLogger`1 updateLogger)
       at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryStore.ExecuteTransaction(IList`1 entries, IDiagnosticsLogger`1 updateLogger)
       at Microsoft.EntityFrameworkCore.InMemory.Storage.Internal.InMemoryDatabase.SaveChangesAsync(IList`1 entries, CancellationToken cancellationToken)
       at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList`1 entriesToSave, CancellationToken cancellationToken)
       at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
       at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
       at Personnel.Infrastructure.Repositories.Persons.PersonWriteRepository.UpdateAsync(Person person, CancellationToken cancellationToken) in C:\Users\user\RiderProjects\staffpro.learn.redkozubov\src\Services\Personnel\Infrastructure\Personnel.Infrastructure.Repositories\Persons\PersonWriteRepository.cs:line 54
       at Personnel.Unit.RepositoryTests.Persons.Write.PersonWriteRepositoryPositiveTests.UpdateAsync_WithWorkExperience_AddsPersonAndContext(Person person, WorkExperienceDto workExperienceDto, PersonWriteRepository writeRepository, PersonReadRepository readRepository) in C:\Users\user\RiderProjects\staffpro.learn.redkozubov\src\Services\Personnel\Tests\Personnel.Unit\RepositoryTests\Persons\Write\PersonWriteRepositoryPositiveTests.cs:line 71
       at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_0.<<InvokeTestMethodAsync>b__1>d.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 276
    --- End of stack trace from previous location ---
       at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func`1 asyncAction) in /_/src/xunit.execution/Sdk/Frameworks/ExecutionTimer.cs:line 48
       at Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in /_/src/xunit.core/Sdk/ExceptionAggregator.cs:line 90
    

    当我尝试使用以下命令将更改保存到实体时,会发生此错误

    await _personnelContext.SaveChangesAsync(cancellationToken) 
    

    尝试更新后 Person 关联实体 WorkExperience .

    代码如下:

    [Theory]
    [MemberData(nameof(ValidTestDataGenerator.GetPersonWithWorkExperienceDataAndRepositories), MemberType = typeof(ValidTestDataGenerator))]
    internal async Task UpdateAsync_WithWorkExperience_AddsPersonAndContext(Person person, WorkExperienceDto workExperienceDto, PersonWriteRepository writeRepository, PersonReadRepository readRepository)
    {
        // Arrange
        await writeRepository.AddAsync(person);
    
        var dbPerson = await readRepository.GetByIdAsync(person.Id);
    
        dbPerson.AddWorkExperience(
            workExperienceDto.Id,
            workExperienceDto.JobTitle,
            workExperienceDto.Street,
            workExperienceDto.Country,
            workExperienceDto.City,
            workExperienceDto.Description,
            workExperienceDto.HiringDate,
            workExperienceDto.OrganizationName,
            workExperienceDto.DismissalDate
        );
    
        // Act
        await writeRepository.UpdateAsync(dbPerson);
    
        // Assert
        dbPerson.Should().BeEquivalentTo(person);
    }
    

    存储库:

    public class PersonReadRepository
    {
        private readonly DbSet<Person> _persons;
    
        public PersonReadRepository(PersonnelContext personnelContext)
        {
            _persons = personnelContext.Set<Person>();
        }
    
        public async Task<Person?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        {
            return await _persons.FindAsync(id, cancellationToken);
        }
    }
    
    public class PersonWriteRepository
    {
        private readonly DbSet<Person> _persons;
        private readonly PersonnelContext _personnelContext;
    
        public PersonWriteRepository(PersonnelContext personnelContext, IPersonReadRepository personReadRepository)
        {
            _personnelContext = personnelContext;
            PersonReadRepository = personReadRepository;
            _persons = personnelContext.Set<Person>();
        }
    
        public IPersonReadRepository PersonReadRepository { get; }
    
        public async Task AddAsync(Person person, CancellationToken cancellationToken = default)
        {
            await _persons.AddAsync(person, cancellationToken);
            await _personnelContext.SaveChangesAsync(cancellationToken);
        }
    
        public async Task UpdateAsync(Person person, CancellationToken cancellationToken = default)
        {
            _persons.Update(person);
            await _personnelContext.SaveChangesAsync(cancellationToken);
        }
    }
    

    可能的原因是什么 DbUpdateConcurrencyException 在这种情况下出现错误,我该如何修复它?我应该实现乐观并发控制,还是我处理实体更新或状态跟踪的方式有问题?

    错误 DbUpdate并发异常 当我打电话的时候

    await _personnelContext.SaveChangesAsync(cancellationToken);
    

    里面 UpdateAsync 方法 PersonWriteRepository .

    我添加了一个 通过以下方式访问数据库 AddAsync 。然后我更新 通过添加一些 工作经历 个人导航属性中的数据 ICollection<WorkExperience> WorkExperiences .

    更新失败,出现上述错误。

    在更新之前,我检查了实体是否处于正确的状态,但仍然得到错误。

    2 回复  |  直到 5 月前
        1
  •  0
  •   sa-es-ir    5 月前

    错误 Attempted to update or delete an entity that does not exist in the store 正在说 WorkExperience 数据库中不存在,因为更新时EF Core试图找到导航的主键,但在您的代码中,您似乎用任意Id向Person添加了一个子项(WorkExperience)。

    因此,有两种解决方案:

    1-一次性将Person及其子项插入数据库:

    // Arrange
    person.AddWorkExperience(
        workExperienceDto.Id,
        workExperienceDto.JobTitle,
        workExperienceDto.Street,
        workExperienceDto.Country,
        workExperienceDto.City,
        workExperienceDto.Description,
        workExperienceDto.HiringDate,
        workExperienceDto.OrganizationName,
        workExperienceDto.DismissalDate
    );
    
    // add Person and its children
    await writeRepository.AddAsync(person);
    
    var dbPerson = await readRepository.GetByIdAsync(person.Id);
    
    // rest of the code
    

    2-首先添加WorkExperience,然后使用主键

    关键是你不能指望EF Core使用Update方法插入子项。

        2
  •  0
  •   Ricardo Peres    5 月前

    这是因为乐观并发检查失败了,这意味着在将实体加载到EF后,它在数据库中被修改了。检查a [Timestamp] 类中的属性或代码配置中的类似属性,在SQL Server中通常有 Timestamp/Rowversion 桌子上的柱子。