从数据库中获取插入 ID 的问题
Problem with getting inserted Id from database
我试图通过 Entity Framework 从数据库中获取最后插入的 ID,但是我的问题有点独特,我找不到任何解决方案,只能重写我的整个基础架构和业务层,所以我所有的 ID 都是 Guid,我可以手动创建,或者在提交后通过另一个数据库调用获取最后一条记录。
问题来了。我有一个三层架构,我在其中使用 UoW、存储库、服务和外观。我会从上到下展示我的代码,这样你就明白了。
这是我的门面,uow.Commit
正在调用 SaveChanges()
public async Task<int> RegisterUserAsync(UserCreateDto user)
{
using (var uow = UnitOfWorkProvider.Create())
{
var id = _userService.Create(user);
await uow.Commit();
return id;
}
}
如你所见,我只将我的 DTO 发送到服务中,我的处理过程是这样的,我也在服务内部进行映射
public virtual int Create(TCreateDto entityDto)
{
var entity = Mapper.Map<TEntity>(entityDto);
Repository.Create(entity);
return entity.Id;
}
最后我的存储库看起来像这样
public TKey Create(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
return entity.Id;
}
是否有一些优雅的解决方案?就像我说的,我唯一的想法是将所有 ID 切换为 Guid 或在提交后第二次调用 Id,我发现这不是很好的解决方案,因为当我想在一个事务中连接两个或多个表时,这是不可能的。
问题的 EF Core 解决方案很简单 - 实体实例的自动生成 PK 可用 在 调用 SaveChanges[Async]
之后。例如
var entity = Mapper.Map<TEntity>(entityDto);
Context.Add(entity);
// Here entity.Id contains auto-generated temporary value
// Other stuff...
Context.SaveChanges();
// Here entity.Id contains actual auto-generated value from the db
所以问题更多地出在基础设施的设计上——所有这些(不必要的)UoW、存储库、服务和外观都只是隐藏了该功能。
我在您的架构中看到的唯一相对简单和优雅的解决方案是将服务 return 类型从 int
更改为 Func<int>
,例如
public virtual Func<int> Create(TCreateDto entityDto)
{
var entity = Mapper.Map<TEntity>(entityDto);
Repository.Create(entity);
return () => entity.Id; // <--
}
然后在您的外观中您可以使用
public async Task<int> RegisterUserAsync(UserCreateDto user)
{
using (var uow = UnitOfWorkProvider.Create())
{
var id = _userService.Create(user);
await uow.Commit();
return id(); // <-- the actual id!
}
}
编辑:
实际上,EF Core 提供了另一种选择,它允许您保持当前设计的完整性 - HiLo key generation strategy, but only if the database provider supports it. I can say for sure Microsoft.EntityFrameworkCore.SqlServer and Npgsql.EntityFrameworkCore.PostgreSQL 分别通过 ForSqlServerUseSequenceHiLo
和 ForNpgsqlUseSequenceHiLo
流畅的 API 支持它。
我试图通过 Entity Framework 从数据库中获取最后插入的 ID,但是我的问题有点独特,我找不到任何解决方案,只能重写我的整个基础架构和业务层,所以我所有的 ID 都是 Guid,我可以手动创建,或者在提交后通过另一个数据库调用获取最后一条记录。
问题来了。我有一个三层架构,我在其中使用 UoW、存储库、服务和外观。我会从上到下展示我的代码,这样你就明白了。
这是我的门面,uow.Commit
正在调用 SaveChanges()
public async Task<int> RegisterUserAsync(UserCreateDto user)
{
using (var uow = UnitOfWorkProvider.Create())
{
var id = _userService.Create(user);
await uow.Commit();
return id;
}
}
如你所见,我只将我的 DTO 发送到服务中,我的处理过程是这样的,我也在服务内部进行映射
public virtual int Create(TCreateDto entityDto)
{
var entity = Mapper.Map<TEntity>(entityDto);
Repository.Create(entity);
return entity.Id;
}
最后我的存储库看起来像这样
public TKey Create(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
return entity.Id;
}
是否有一些优雅的解决方案?就像我说的,我唯一的想法是将所有 ID 切换为 Guid 或在提交后第二次调用 Id,我发现这不是很好的解决方案,因为当我想在一个事务中连接两个或多个表时,这是不可能的。
问题的 EF Core 解决方案很简单 - 实体实例的自动生成 PK 可用 在 调用 SaveChanges[Async]
之后。例如
var entity = Mapper.Map<TEntity>(entityDto);
Context.Add(entity);
// Here entity.Id contains auto-generated temporary value
// Other stuff...
Context.SaveChanges();
// Here entity.Id contains actual auto-generated value from the db
所以问题更多地出在基础设施的设计上——所有这些(不必要的)UoW、存储库、服务和外观都只是隐藏了该功能。
我在您的架构中看到的唯一相对简单和优雅的解决方案是将服务 return 类型从 int
更改为 Func<int>
,例如
public virtual Func<int> Create(TCreateDto entityDto)
{
var entity = Mapper.Map<TEntity>(entityDto);
Repository.Create(entity);
return () => entity.Id; // <--
}
然后在您的外观中您可以使用
public async Task<int> RegisterUserAsync(UserCreateDto user)
{
using (var uow = UnitOfWorkProvider.Create())
{
var id = _userService.Create(user);
await uow.Commit();
return id(); // <-- the actual id!
}
}
编辑:
实际上,EF Core 提供了另一种选择,它允许您保持当前设计的完整性 - HiLo key generation strategy, but only if the database provider supports it. I can say for sure Microsoft.EntityFrameworkCore.SqlServer and Npgsql.EntityFrameworkCore.PostgreSQL 分别通过 ForSqlServerUseSequenceHiLo
和 ForNpgsqlUseSequenceHiLo
流畅的 API 支持它。