[英]What's the point of multiple SaveChanges inside an entity-framework-core transaction?
[英]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();
其中ParentTable
和ChildTable
是兩個與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.