从数据库获取插入的ID出现问题

3
我正在尝试通过Entity Framework从数据库获取最后一个插入的Id,但我的问题是独一无二的,我找不到任何解决方案,只能重写整个基础架构和业务层,以便所有的Id都是Guids,我可以手动创建,或在提交后通过另一个数据库调用获取最后一条记录。
这是问题所在。我有一个三层架构,我使用UoW、Repository、Services和Facades。我将从上到下展示我的代码,这样你就可以理解。
这是我的Facade,其中uw.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,但我认为这不是很好的解决方案,因为当我想要在一个事务中连接两个或更多表时将无法实现。


我认为应该有一种方法让你的数据库处理GUID的创建。 - diogenesgg
1
Guid的创建不是问题,您可以让数据库自动创建它,但在这种情况下,手动创建会更有效,因为您将知道插入哪个ID而无需提交。但那是我的备选方案,我只想知道是否有一种使用整数ID的方法。 - xpoproci
如果您对存储过程感到满意,那么您可以编写一些原始的 SQL 代码,使用输出子句返回插入的 ID。 - Steve
1个回答

5

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,存储库,服务和门面只是隐藏了该功能。
我在您的架构中看到的唯一相对简单且优雅的解决方案是将服务返回类型从int更改为Func,例如。
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键生成策略,但前提是数据库提供程序支持它。我可以肯定地说,Microsoft.EntityFrameworkCore.SqlServerNpgsql.EntityFrameworkCore.PostgreSQL分别使用ForSqlServerUseSequenceHiLoForNpgsqlUseSequenceHiLo流畅的 API 支持此功能。


谢谢,这很有帮助,但是仍然有一个问题。当我在一个事务中插入多个需要相互引用的实体时,这个解决方案仍然无法提供我需要添加的ID。我知道EF有这个功能,但我之所以这样做,是因为我可以通过改变最少的代码来切换ORM。 - xpoproci
改变ORM永远不会容易。无论如何,EF通过使用导航属性而不是ID将实体链接起来解决了ID问题。然而,我相信它允许您在一个上下文事务中将实体的临时ID用作另一个实体中的FK。 SaveChanges会相应地更新所有内容。 - Ivan Stoev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接