我正在更新一位前同事的一些現有代碼,并遇到一個奇怪的問題,即未使用的行導致 Entity Framework 出現錯誤。如果我注釋掉標記為 的代碼//This Line!,則一切正常。
foreach (Place item in ListOfPlaces)
{
//This line!
List<Place> PlacesList = context.Places.Where(x => x.PlaceNumberID == item.PlaceNumberID).ToList();
long PlaceId = context.Places
.Where(x => x.PlaceNumberID == item.PlaceNumberID)
.Select(x => x.PlaceId)
.FirstOrDefault();
if (PlaceId != 0)
{
item.ID = PlaceId;
context.Places.Attach(item);
context.Entry(item).State = System.Data.Entity.EntityState.Modified;
}
}
如果我包含該行,則會在該Attach(item)行中看到此處顯示的錯誤:
附加“地點”型別的物體失敗,因為同一型別的另一個物體已具有相同的主鍵值。如果圖中的任何物體具有沖突的鍵值,則在使用“Attach”方法或將物體狀態設定為“Unchanged”或“Modified”時可能會發生這種情況。這可能是因為某些物體是新的,尚未收到資料庫生成的鍵值。在這種情況下,使用“添加”方法或“添加”物體狀態來跟蹤圖形,然后根據需要將非新物體的狀態設定為“未更改”或“已修改”。
我知道如何從代碼的角度解決這個問題(洗掉該行!),但是如果有人可以解釋一下,我無法弄清楚為什么它會破壞應用程式。
uj5u.com熱心網友回復:
我不明白為什么它破壞了應用程式
在我看來,這條線會導致下載一些 ID 為 N 的地方 - 看到最后的 ToList 了嗎?它將觸發查詢以運行和下載資料。EF 從它接收到的每一行創建物件,因為這是默認行為(它可以被禁用,例如 AsNoTracking)
稍后您嘗試創建另一個具有相同主鍵值的物件并將其附加到背景關系,但背景關系已經知道某個 ID 為 123 的物件(例如),因為第一行導致它已被下載/跟蹤,因此您得到當您嘗試關聯另一個時出現錯誤 - 如果 EF 允許兩者都進入其跟蹤記憶體,則它不知道哪個是應該保存回資料庫的記錄的真正權限
我相信您的臨時查詢不會導致問題,因為它不會觸發整個物體的下載,因為它只是檢索 ID
如果您嘗試實作 insert-if-not-exists 樣式行為,您應該嘗試使用一些 XOrDefault 或 Find 下載 ID 為 x 的物體,如果結果為空/默認,則創建并添加一個新物體(您不需要附加)。本質上,去掉第一行,只做 ID 檢查,如果回傳的 ID 是默認的,做一個context.Places.Add(new Place{...}).
如果您正在尋找 upsert,下載整個物體然后檢查它是否是默認的可能是最簡單的;如果是,則創建一個新的,否則編輯下載的一個。
如果您正在嘗試“無需下載即可更新”,則完全跳過查詢部分并附加您宣告為已修改的物體。
如果您在沒有下載的情況下遇到一些混合故障,我認為您會很掙扎,因為在您決定要做什么之前,您至少必須詢問資料庫是否知道物體......或者您運行原始 MERGE
uj5u.com熱心網友回復:
整個回圈毫無意義。你重復同樣的兩次。一旦您選擇了其中一項,EF 就會將其標記為已跟蹤。并且您無法使用其他專案進行更新,否則第一個專案將被取消跟蹤,或者您可以使用已跟蹤專案。
試試這個代碼
foreach (Place item in ListOfPlaces)
{
var placesList = context.Places.Where(x => x.PlaceNumberID == item.PlaceNumberID).ToList();
if(placesList!=null && placesList.Count ==1)
{
var existedPlace = placesList.FirstOrDefault();
context.Entry(existedPlace).CurrentValues.SetValues(item);
}
// and maybe this
else context.Places.Add(item)
}
context.SaveChanges();
更新
感謝@CaiusJard 的提示,在這種情況下,使用 SingleOrDefault 而不是 ToList 效率更高
.....
var existedPlace = context.Places.Where(x => x.PlaceNumberID == item.PlaceNumberID).SingleOrDefault();
if(existedPlace!=null)
{
context.Entry(existedPlace).CurrentValues.SetValues(item);
}
.....
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/313307.html
下一篇:GetCustomAttributes和[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
