[英].NET 6 Entity Framework Tracking with Dependency Injection
我想我只是不了解 EF 跟蹤。 我通過依賴注入添加了上下文:
builder.Services.AddDbContext<OracleContext>(options => options.UseOracle(OracleConnectionString, b => b.UseOracleSQLCompatibility("11"))
.LogTo(s => System.Diagnostics.Debug.WriteLine(s))
.EnableDetailedErrors(Settings.Dev_System)
.EnableSensitiveDataLogging(Settings.Dev_System)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
我在這里將跟蹤行為設置為 NoTracking(至少我是這么認為的)。
我有一個 .NET Controller,它的構造函數中有上下文。 它將此上下文傳遞給包含我的方法的 class 構造函數。 幾乎一切正常......除了一個:
我有一個執行 context.RawSqlQuery 的方法來獲取對象列表。 我迭代這些對象,從以相同方式(使用注入上下文)生成的不同 class 調用兩個單獨的方法。 此方法首先執行 EF 查詢以驗證 object 是否不存在,如果存在則返回它,我們繼續 - 沒有問題。 在檢查它是否存在的查詢中,我還為 SnG 添加了 AsNoTracking()。 但是,如果 object 不存在,我會嘗試創建一個新的......每次我做一個 context.add 我得到
“無法跟蹤實體類型 'Whatever' 的實例,因為已跟蹤具有鍵值 '{MfrId: 90}' 的另一個實例。附加現有實體時,確保只附加一個具有給定鍵值的實體實例。 “
我嘗試添加 db.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking - 沒有變化我嘗試添加 context.Entry(NewItem).State = EntityState.Detached; 通話前后 - 沒有變化。 我在上下文中嘗試了一個循環,該循環獲取所有被跟蹤的對象並將它們設置為分離 - 沒有變化。
我在這里錯過了什么? 首先 - 它為什么要跟蹤? 第二 - 關於如何通過這個的任何建議? 我是否應該放棄對上下文使用依賴注入(糟透了……為此需要大量返工)?
根據要求 - 這是失敗的 class 和方法(刪除了不相關的內容):
public class AssetMethods : IAssetMethods
{
public OracleContext db;
public AssetMethods(OracleContext context)
{
db = context;
}
public CcpManufacturer? CreateNewManufacturer(CcpManufacturer NewMan, string ActorID)
{
...blah blah non DB validation stuff removed...
//Check if it exists already
CcpManufacturer? OldMan = db.CcpManufacturers.Where(m=>m.MfrName == NewMan.MfrName).AsNoTracking().FirstOrDefault();
if (OldMan != null) {
return OldMan;
}
//Who done did it
NewMan.CreatedBy = ActorID;
NewMan.CreationDate = DateTime.Now;
NewMan.Isenabled = true;
//save
db.CcpManufacturers.Add(NewMan);
db.SaveChanges(); //fails here
//Prevent XSS Reflection
return db.CcpManufacturers.Find(NewMan.MfrId);
}
}
從此代碼調用此方法。 OM 也在使用注入的上下文
List<MasterItem> Items = OM.RawSqlQuery(Query, x => new MasterItem { MFR_NAME = (string)x[0], MODEL_NUMBER = (string)x[1], LEGAL_NAME= (string)x[2]});
foreach (MasterItem item in Items)
{
CcpManufacturer? Man = new() {
MfrName = item.MFR_NAME,
Displayname = item.MFR_NAME
};
Man = AM.CreateNewManufacturer(Man,System.Id); //if its new.. it never get passed here because of the discussed error...
if (Man == null || Man.MfrId == 0)
{
continue;
}
.... other stuff
}
因此,mfr id 被添加到一個新的 object 中,該 object 被傳遞給一個幾乎相同的方法來創建一個項目(其中附有 mfr id)。 現在 - 如果我拆下那個項目 - 我沒問題。 但是,當我幾乎在所有地方都將其關閉時,為什么它仍在跟蹤?
是的,你發現了你的問題。
關閉跟蹤會影響 EF 在查詢實體時執行的操作。 這意味着當我告訴 EF 從數據庫中讀取數據並給我實體時,它不會掛在這些實體的引用上。
但是,無論您的跟蹤設置如何,您告訴 DBContext 添加到 DbSet 的實體和相關實體都將被跟蹤。
所以如果我做類似的事情:
var entity1 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
var entity2 = context.Entities.Single(x => x.Id == entityId).AsNoTracking();
對 entity1 和 entity2 的引用將是對同一記錄的 2 個不同引用。 兩者都是分離的,因此 DbContext 不會跟蹤它們中的任何一個。 我可以使用並附加它們中的任何一個來執行更新,但是從那時起該實體將被視為已附加,直到我再次明確分離它。 嘗試使用其他參考進行更新將導致該錯誤。 如果我在附加並更新第一個實體引用后查詢指定 NoTracking,我將取回一個新的未跟蹤實體引用。 DbContext 不會返回它的跟蹤引用,但它也不會丟棄它。
如果我添加一個新實體然后查詢它並指定 NoTracking,則會發生完全相同的事情。 查詢返回未跟蹤的引用。 因此,如果您嘗試附加它來更新一行,EF 將抱怨它已經在跟蹤的引用。
我不建議深入繞過分離實體的兔子洞,除非你熱衷於花時間真正了解幕后發生的事情,並為非常慎重和謹慎地處理引用做好准備。 其影響不僅僅是事情沒有按預期工作,它是讓事情在完全情境的基礎上工作或不工作,這可能是一件令人討厭的調試和修復事情,即使你知道要尋找什么。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.