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);
我有一个 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);