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

41ik7eoe  于 2022-12-15  发布在  .NET
关注(0)|答案(7)|浏览(177)

在实体框架中-在调用“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();
nqwrtyyt

nqwrtyyt1#

ID由数据库在将行插入到表中之后生成。在插入行之前,您不能询问数据库该值是什么。
有两种方法可以解决这个问题--最简单的方法是调用SaveChanges,因为您是在事务内部,所以在获得ID后,如果出现问题,您可以回滚。
第二种方法是不使用数据库内置的IDENTITY字段,而是自己实现它们。当你有很多批量插入操作时,这会非常有用,但它也有代价--实现起来并不容易。
编辑:SQL Server 2012有一个内置的SEQUENCE类型,可以用来代替IDENTITY列,不需要自己实现它。

c9x0cxw0

c9x0cxw02#

正如其他人已经指出的,在调用saveChanges()之前,您无法访问数据库生成的增量值--然而,如果您只对id感兴趣,将其作为连接到另一个实体(例如,在同一事务中)的一种手段,那么您还可以依赖EF Core分配的临时ID:
根据所使用的数据库提供程序,值可以由EF在客户端生成,也可以在数据库中生成。如果值由数据库生成,则在将实体添加到上下文时,EF可能会分配一个临时值。然后,在SaveChanges()过程中,此临时值将被数据库生成的值替换。
下面是一个例子来演示它是如何工作的,假设MyEntityMyOtherEntity通过属性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

当通过导航属性(即y.MyEntity = x而不是y.MyEntityId = x.Id)分配引用时,上述方法也适用

svmlkihl

svmlkihl3#

如果你的tblTest实体连接到你想要附加的其他实体,你不需要ID来创建关系。假设tblTest被附加到另一个Test对象,就像在另一个Test对象中你有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等。

njthzxwz

njthzxwz4#

@zmbq是对的,调用保存changes后才能得到id。
我的建议是,你不应该依赖于生成的ID的数据库。数据库应该只是一个细节,你的应用程序,而不是一个完整的和不可更改的一部分。
如果你不能解决这个问题,使用GUID作为一个标识符,因为它的唯一性。MSSQL支持GUID作为一个本地列类型,它的速度很快(虽然没有比INT快)。
干杯

vmjh9lq9

vmjh9lq95#

在调用.SaveChanges()之前,您可以使用Hi/Lo算法检索ID。一旦对象被添加到dbcontext中,ID就会被分配给对象。
使用Fluent API的配置示例:

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

相关Microsoft文章的摘录:
当您在提交更改之前需要唯一键时,Hi-Lo算法非常有用。总之,Hi-Lo算法为表行分配唯一标识符,而不需要立即将行存储在数据库中。这样,您就可以立即开始使用标识符,就像常规顺序数据库ID一样。

p5cysglq

p5cysglq6#

解决此问题的简单方法是

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键连接的两个表。

pxq42qpu

pxq42qpu7#

您可以在ChangeTracker中查找该值,如下所示:

var newEntity = new MyEntity();
var newEntity.Property = 123;
context.Add(newEntity);

//specify a logic to identity the right entity here:
var entity = context.ChangeTracker.Entries()
    .FirstOrDefault(e => e.Entity is MyEntity myEntity && 
        myEntity.Property == newEntity.Property);
//In this case we look up the value for an autogenerated id of type int/long
 //it will be a negative value like -21445363467
var value = entity.Properties?
    .FirstOrDefault(pe => pe.Metadata.GetColumnName() == nameof(MyEntity.Id))?.CurrentValue;    
//if you set it on another entity, it will be replaced on SaveChanges()

我的设置是mysql5.7,但应该在其他环境中也工作。

相关问题