EF Core 在父实体保存时更新子实体

EF Core upsert child entities upon parent entity save

我有一个 DDD 聚合,其中 User 作为根记录,Appointment 作为子记录。我想,当我在我的存储库中保存一个用户时,更新该用户的现有子约会并插入数据库中不存在的子约会。

I have for each entity a domain class and a persistence class

我已阅读 this post 关于此事的内容,我想我明白接受的答案解释了什么,所以我遵循以下逻辑:

    public async Task Update(IApplicationUserWithAppointments domainUserEntity)
    {
        ApplicationUserEntity persistenceUserEntity = await FindEntityById(domainUserEntity.Id);

        IDictionary<Guid, AppointmentEntity> appointmentEntitiesById =
            persistenceUserEntity.Appointments
                .ToDictionary(appointmentEntity => appointmentEntity.Id, appointmentEntity => appointmentEntity);

        persistenceUserEntity.UserName = domainUserEntity.UserName;
        persistenceUserEntity.Password = domainUserEntity.Password;
        persistenceUserEntity.FirstName = domainUserEntity.FirstName;
        persistenceUserEntity.LastName = domainUserEntity.LastName;
        persistenceUserEntity.Role = domainUserEntity.Role;
        persistenceUserEntity.Validated = domainUserEntity.Validated;
        persistenceUserEntity.Appointments = domainUserEntity.Appointments
            .Select(appointment => BuildOrUpdateAppointmentEntity(appointmentEntitiesById, appointment))
            .ToList();

        this.context.Users.Update(persistenceUserEntity);
    }

    private static AppointmentEntity BuildOrUpdateAppointmentEntity(IDictionary<Guid, AppointmentEntity> appointmentEntitiesById,
        Appointment appointment)
    {
        if (!appointmentEntitiesById.ContainsKey(appointment.Id))
        {
            return new AppointmentEntity(appointment);
        }

        AppointmentEntity appointmentEntity = appointmentEntitiesById[appointment.Id];
        appointmentEntity.State = appointment.State.Name;
        appointmentEntity.DateTime = appointment.DateTime;
        return appointmentEntity;
    }

逻辑是我从数据库中检索用户实体及其约会(以避免分离实体错误)。然后,我映射约会实体,更新现有实体并创建新实体。

此逻辑适用于更新现有约会记录,但对于插入新约会记录,以下单元测试失败:

    public async Task Update_ChildRecord_InsertChildRecordInDb()
    {
        // Given
        ApplicationUserEntity entity = await this.dbDataFactory.InsertValidatedLifeAssistant();
        var repository = new ApplicationUserRepository(this.context, factory);

        entity.Appointments.Add(new AppointmentEntity()
        {
            Id = Guid.NewGuid(),
            State = "Planned",
            DateTime = DateTime.Today.AddDays(3)
        });
        
        // When
        await repository.Update(entity.ToDomainEntity(new AppointmentStateFactory()));
        await repository.Save();

        // Then
        entity = await this.context
            .Users
            .Include(u => u.Appointments)
            .FirstAsync(item => item.Id == entity.Id);
        (await this.context.Appointments.CountAsync()).Should().Be(1);
    }

出现以下错误:

The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

Save 调用更新时。

我不明白为什么我的逻辑不起作用。提前谢谢你

经过大量调试,感谢 github post 我意识到我的问题是我的子记录已经生成了一个 ID,而 EF Core 并没有预料到它。我通过在 onModelCreating 模型定义中使用 ValueGeneratedNever() 解决了我的问题。例如:

        modelBuilder.Entity<AppointmentEntity>().HasKey(appointment => appointment.Id);
        modelBuilder.Entity<AppointmentEntity>().Property(appointment => appointment.Id).ValueGeneratedNever();
        modelBuilder.Entity<AppointmentEntity>().Property(appointment => appointment.State);
        modelBuilder.Entity<AppointmentEntity>().Property(appointment => appointment.DateTime);