簡體   English   中英

由於一個或多個外鍵屬性不可為空,因此無法更改關系

[英]The relationship could not be changed because one or more of the foreign-key properties is non nullable

在使用EF更新期間出現以下錯誤:

操作失敗:無法更改關系,因為一個或多個外鍵屬性不可為空。 當對關系進行更改時,相關的外鍵屬性將設置為空值。 如果外鍵不支持空值,則必須定義新關系,必須為外鍵屬性分配另一個非空值,或者必須刪除不相關的對象。

有沒有一般的方法來查找哪些外鍵屬性導致上述錯誤?

[更新]

對於以下代碼導致上述錯誤的一種情況(我在斷開連接的環境中工作,所以我使用graphdiff來更新我的對象圖),當它想要運行_uow.Commit();

public void CopyTechnicalInfos(int sourceOrderItemId, List<int> targetOrderItemIds)
{
  _uow = new MyDbContext();
   var sourceOrderItem = _uow.OrderItems
          .Include(x => x.NominalBoms)
          .Include("NominalRoutings.NominalSizeTests")
          .AsNoTracking()
          .FirstOrDefault(x => x.Id == sourceOrderItemId);


   var criteria = PredicateBuilder.False<OrderItem>();
   foreach (var targetOrderItemId in orderItemIds)
   {
      int id = targetOrderItemId;
      criteria = criteria.OR(x => x.Id == id);
   }
   var targetOrderItems = _uow.OrderItems
                              .AsNoTracking()
                              .AsExpandable()   
                              .Where(criteria)
                              .ToList();

  foreach (var targetOrderItem in targetOrderItems)
  {
        //delete old datas and insert new datas 
        targetOrderItem.NominalBoms = sourceOrderItem.NominalBoms;
        targetOrderItem.NominalBoms.ForEach(x => x.Id = 0);

        targetOrderItem.NominalRoutings = sourceOrderItem.NominalRoutings;
        targetOrderItem.NominalRoutings.ForEach(x => x.Id = 0);
        targetOrderItem.NominalRoutings
                       .ForEach(x => x.NominalTests.ForEach(y => y.Id = 0));
        targetOrderItem.NominalRoutings
                       .ForEach(x => x.NominalSizeTests.ForEach(y => y.Id = 0));
       _uow.OrderItems.UpdateGraph(targetOrderItem, 
                                   x => x.OwnedCollection(y => y.NominalBoms)
                                         .OwnedCollection(y => y.NominalRoutings, 
                                          with => with
                                         .OwnedCollection(t => t.NominalTests)));
   }
   _uow.Commit();
}

在Entity Framework中,您可以使用外鍵關聯 也就是說,另一個對象的外鍵表示為一對兩個屬性:原始外鍵屬性(例如NominalRouting.OrderItemId )和對象引用( NominalRouting.OrderItem )。

這意味着您可以設置原始值或對象引用以建立外鍵關聯。 如果您設置其中一個,EF會盡可能保持另一個同步。 不幸的是,這也可能引起原始外鍵值與其伴隨參考之間的沖突。

很難說出你的情況究竟發生了什么。 不過,我知道你從一個父母的另一個“復制”對象的方法是......不理想。 首先,更改主鍵值絕不是一個好主意。 通過將它們設置為0您可以使對象看起來像新的,但它們不是。 其次,您將多個相同的子對象分配給其他父對象。 認為 ,作為一個結果,你最終得到大量具有外鍵值 ,但不是一個參考對象。

我說“復制”,因為那是你看似試圖實現的。 如果是這樣,您應該正確克隆對象並將它們Add到每個targetOrderItem 與此同時,我想知道為什么你(顯然)克隆所有這些對象。 看起來多對多關聯在這里更合適。 但這是一個不同的主題。

現在你的實際問題是: 如何找到沖突的關聯?

這非常非常困難。 它需要代碼來搜索概念模型並找到涉及外鍵關聯的屬性。 然后你必須找到他們的價值觀並發現不匹配。 與確定可能的沖突何時是真正的沖突相比,這很難,但是微不足道。 讓我通過兩個例子來澄清這一點。 這里, OrderItem類具有由OrderOrderId屬性組成的必需外鍵關聯。

var item = new OrderItem { OrderId = 1, ... };
db.OrderItems.Add(item);
db.SaveChanges();

所以有一個分配了OrderIdOrder = null的項目,EF很高興。

var item = db.OrderItems.Include(x => x.Order).Find(10);
// returns an OrderItem with OrderId = 1
item.Order = null;
db.SaveChanges();

同樣,分配了OrderIdOrder = null的項目,但EF拋出異常“關系無法更改...”。

(還有更多可能的沖突情況)

因此,在OrderId/Order對中查找不匹配的值是不夠的,您還必須檢查實體狀態並確切地知道不允許不匹配的狀態組合。 我的建議:忘記它,修復你的代碼。

雖然有一個骯臟的伎倆。 當EF嘗試匹配外鍵值和引用,某處的嵌套樹深跌if的IT收集我們談論到的一個成員變量沖突ObjectStateManager ,命名_entriesWithConceptualNulls 通過做一些反思可以獲得它的價值:

#if DEBUG

db.ChangeTracker.DetectChanges(); // Force EF to match associations.
var objectContext = ((IObjectContextAdapter)db).ObjectContext;
var objectStateManager = objectContext.ObjectStateManager;
var fieldInfo = objectStateManager.GetType().GetField("_entriesWithConceptualNulls", BindingFlags.Instance | BindingFlags.NonPublic);
var conceptualNulls = fieldInfo.GetValue(objectStateManager);

#endif

conceptualNulls是一個HashSet<EntityEntry>EntityEntry是一個內部類,因此您只能檢查調試器中的集合以了解沖突的實體。 僅用於診斷目的!!!

暫無
暫無

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

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