实体框架 - 在事务内的"SaveChanges"之前检索ID

use*_*635 54 c# asp.net entity-framework

在实体框架中 - 在调用"SaveChanges"之前,有没有办法在事务中检索新创建的ID(标识)?

我需要第二个插入的ID,但它总是返回0 ...

        ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext;

        objectContext.Connection.Open();

        using (var transaction = objectContext.Connection.BeginTransaction())
        {
            foreach (tblTest entity in saveItems)
            {
                this.context.Entry(entity).State = System.Data.EntityState.Added;
                this.context.Set<tblTest>().Add(entity);

                int testId = entity.TestID;

                .... Add another item using testId
            }

            try
            {
                context.SaveChanges();
                transaction.Commit();
            }
            catch (Exception ex)
            {
                transaction.Rollback();
                objectContext.Connection.Close();
                throw ex;
            }
        }

        objectContext.Connection.Close();
Run Code Online (Sandbox Code Playgroud)

zmb*_*mbq 45

将行插入表后,数据库将生成ID.在插入行之前,您无法询问数据库该值的内容.

你有两种方法 - 最容易打电话SaveChanges.由于您在交易中,因此在您获得ID后可以回滚以防出现问题.

第二种方法是不使用数据库的内置IDENTITY字段,而是自己实现它们.当你有大量的批量插入操作时,这可能非常有用,但它需要付出代价 - 实现起来并非易事.

编辑:SQL Server 2012有一个内置的SEQUENCE类型,可以用来代替IDENTITY列,不需要自己实现它.

  • 当然,这就是交易的内容.事务中的每个操作都将在`rollback`上回滚.看看http://en.wikipedia.org/wiki/ACID (6认同)
  • .NET在TransactionScope上有一个非常好的回滚功能.这就够了. (5认同)
  • 如果我在第一次插入后调用“SaveChanges”,那么第二次插入失败 - 事务是否仍会回滚第一次插入? (2认同)

Lau*_*kas 8

可以.SaveChanges()在呼叫前使用 Hi/Lo 算法检索 ID 。一旦对象被添加到 dbcontext 中,该 id 就会被分配给该对象。

使用 Fluent api 的示例配置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Entity>(e =>
    {
        e.Property(x => x.Id).UseHiLo();
    });
}
Run Code Online (Sandbox Code Playgroud)

相关微软文章的摘录:

当您在提交更改之前需要唯一的密钥时,Hi/Lo 算法非常有用。总之,Hi-Lo 算法为表行分配唯一标识符,而不依赖于立即将行存储在数据库中。这使您可以立即开始使用标识符,就像使用常规顺序数据库 ID 一样。


Ser*_*usM 6

@zmbq是对的,你只能在调用保存更改后获取id.

我的建议是你不应该依赖于生成的数据库ID.数据库应该只是应用程序的细节,而不是一个完整且不可更改的部分.

如果您无法解决该问题,请使用GUID作为标识符,因为它的唯一性.MSSQL支持GUID作为本机列类型,并且速度快(虽然不比INT快).

干杯


B12*_*ter 6

正如其他人已经指出的那样,您无权访问数据库在saveChanges()调用之前生成的增量值–但是,如果您仅对id作为与另一个实体(例如,在同一事务中)建立连接的方式感兴趣,则您还可以依靠EF Core分配的临时ID

根据所使用的数据库提供程序,值可以由EF在客户端或在数据库中生成。如果该值是由数据库生成的,则当您将实体添加到上下文时EF可能会分配一个临时值。然后,在SaveChanges()期间,此临时值将由数据库生成的值替换。

这是一个示例,演示其工作原理。说MyEntity是通过MyOtherEntityvia属性引用的,MyEntityId该属性需要在saveChanges调用之前分配。

var x = new MyEntity();        // x.Id = 0
dbContext.Add(x);              // x.Id = -2147482624 <-- EF Core generated id
var y = new MyOtherEntity();   // y.Id = 0
dbContext.Add(y);              // y.Id = -2147482623 <-- EF Core generated id
y.MyEntityId = x.Id;           // y.MyEntityId = -2147482624
dbContext.SaveChangesAsync();
Debug.WriteLine(x.Id);         // 1261 <- EF Core replaced temp id with "real" id
Debug.WriteLine(y.MyEntityId); // 1261 <- reference also adjusted by EF Core
Run Code Online (Sandbox Code Playgroud)

通过导航属性(即y.MyEntity = x代替y.MyEntityId = x.Id

  • 我认为这应该是公认的答案,因为问题正是与此相关:获取需要此尚未插入的行/实体的下一个插入的 Id。感谢@B12Toaster 的美好发现 (2认同)

Arm*_*men 5

如果您的tblTest实体已连接到要附加的其他实体,则不需要Id即可创建关系。可以说tblTest附加到anotherTest对象,这就是在anotherTest对象中具有tblTest对象和tblTestId属性的方式,在这种情况下,您可以拥有以下代码:

using (var transaction = objectContext.Connection.BeginTransaction())
    {
        foreach (tblTest entity in saveItems)
        {
            this.context.Entry(entity).State = System.Data.EntityState.Added;
            this.context.Set<tblTest>().Add(entity);

            anotherTest.tblTest = entity;
            ....
        }
    }
Run Code Online (Sandbox Code Playgroud)

提交后,将创建关系,您无需担心Ids等问题。