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

时间:2013-07-08 09:45:55

标签: 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();

5 个答案:

答案 0 :(得分:45)

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

你有两种解决方法 - 最简单的方法是致电SaveChanges。由于您在交易中,因此在收到ID后可以回滚以防出现问题。

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

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

答案 1 :(得分:7)

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

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

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

干杯

答案 2 :(得分:4)

如果您的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;
            ....
        }
    }

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

答案 3 :(得分:2)

As others have already pointed out, you have no access to the increment value generated by the database before saveChanges() was called – however, if you are only interested in the id as a means to make a connection to another entity (e.g. in the same transaction) then you can also rely on temporary ids assigned by EF Core:

Depending on the database provider being used, values may be generated client side by EF or in the database. If the value is generated by the database, then EF may assign a temporary value when you add the entity to the context. This temporary value will then be replaced by the database generated value during SaveChanges().

Here is an example to demonstrate how this works. Say MyEntity is referenced by MyOtherEntity via property MyEntityId which needs to be assigned before saveChanges is called.

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

The above also works when assigning references via navigational properties, i.e. y.MyEntity = x instead of y.MyEntityId = x.Id

答案 4 :(得分:0)

一个简单的解决方法是

var ParentRecord = new ParentTable () {
SomeProperty = "Some Value",
AnotherProperty = "Another Property Value"
};

ParentRecord.ChildTable.Add(new ChildTable () {
ChildTableProperty = "Some Value",
ChildTableAnotherProperty = "Some Another Value"
});

db.ParentTable.Add(ParentRecord);

db.SaveChanges();

ParentTableChildTable是两个用Foregin键连接的表。