簡體   English   中英

實體框架 - 在事務內的“SaveChanges”之前檢索 ID

[英]Entity Framework - retrieve ID before 'SaveChanges' inside a transaction

在 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();

將行插入表后,數據庫將生成ID。 在插入行之前,您無法詢問數據庫該值的內容。

你有兩種解決方法 - 最簡單的方法是調用SaveChanges 由於您在交易中,因此在您獲得ID后可以回滾以防出現問題。

第二種方法是不使用數據庫內置的IDENTITY字段,而是自己實現它們。 當你有大量的批量插入操作時,這可能非常有用,但它需要付出代價 - 實現起來並非易事。

編輯:SQL Server 2012有一個內置的SEQUENCE類型,可以用來代替IDENTITY列,不需要自己實現它。

@zmbq是對的,你只能在調用保存更改后獲取id。

我的建議是你不應該依賴於生成的數據庫ID。 數據庫應該只是應用程序的細節,而不是一個完整且不可更改的部分。

如果您無法解決該問題,請使用GUID作為標識符,因為它的唯一性。 MSSQL支持GUID作為本機列類型,並且速度快(雖然不比INT快)。

干杯

可以使用 Hi/Lo 算法在調用.SaveChanges()之前檢索 ID。 添加到 dbcontext 后,id 將分配給 object。

流利的示例配置 api:

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

微軟相關文章的摘錄:

當您在提交更改之前需要唯一鍵時,Hi/Lo 算法很有用。 總之,Hi-Lo 算法為表行分配唯一標識符,而不依賴於立即將行存儲在數據庫中。 這使您可以立即開始使用標識符,就像使用常規順序數據庫 ID 一樣。

如果您的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等。

正如其他人已經指出的那樣,在調用saveChanges()之前,您無法訪問數據庫生成的增量值 - 但是,如果您只對id作為一種與另一個實體建立連接的方式感興趣(例如,同一事務)那么你也可以依賴EF Core分配的臨時ID

根據所使用的數據庫提供程序,可以通過EF或數據庫在客戶端生成值。 如果該值是由數據庫生成的,則在將實體添加到上下文時EF可以分配臨時值。 然后,在SaveChanges()期間,此臨時值將由數據庫生成的值替換。

這是一個演示如何工作的示例。 MyEntity被引用MyOtherEntity通過屬性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

一個簡單的解決方法是

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鍵連接的表。

您可以像這樣在 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()

我的設置是 mysql 5.7,但也應該適用於其他環境。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM