2012-04-23 221 views
3

我有以下StandardContact實體框架更新許多一對多的關係 - POCO

實體兩個實體RelayConfig之間的許多一對多的關係:

public class RelayConfig : EntityBase, IDataErrorInfo { 
    ... 
    //Associations 
    public virtual ICollection<StandardContact> StandardContacts { get; set; } 
} 


public class StandardContact :EntityBase, IDataErrorInfo { 
    ... 
    //Associations 
    public virtual ICollection<RelayConfig> RelayConfigs { get; set; } 
} 

現在我我試圖更新RelayConfig及其與StandardContact的關係。這是更新RelayConfig的代碼。

public class RelayConfigRepository : GenericRepository<RelayConfig> { 
    .... 

    public void Update(RelayConfig relayConfig, List<StandardContact> addedContacts, List<StandardContact> deletedContacts) { 
     context.RelayConfigs.Add(relayConfig); 
     if (relayConfig.Id > 0) { 
      context.Entry(relayConfig).State = EntityState.Modified; 
     } 

     addedContacts.ForEach(ad => relayConfig.StandardContacts.Add(ad)); 

     foreach (StandardContact standardContact in relayConfig.StandardContacts) { 
      if (standardContact.Id > 0) { 
       context.Entry(standardContact).State = EntityState.Modified; 
      } 
     } 

     relayConfig.StandardContacts.ToList().ForEach(s => { 
      if (deletedContacts.Any(ds => ds.Id == s.Id)) { 
       context.Entry(s).State = EntityState.Deleted; 
      } 
     }); 
    } 
    ... 
} 

當我運行更新時,我收到異常,其內部異常如下。

InnerException: System.Data.SqlClient.SqlException 
     Message=Violation of PRIMARY KEY constraint 'PK__Standard__EE33D91D1A14E395'. Cannot insert duplicate key in object 'dbo.StandardContactRelayConfigs'. 

dbo.StandardContactRelayConfigs是鏈接RelayConfig和StandardContact鏈接表。如您所見,如果Id> 0(更新方法末尾設置的已刪除記錄除外),則更新代碼會將所有實體更改爲已修改狀態。

我真的不明白爲什麼實體框架試圖在鏈接的表中插入行,並與上述例外失敗。我已經將現有的RelayConfig.StandardContacts實體的EntityState更改爲Modified。

總之,爲什麼我會得到上面粘貼的異常。

關於, Nirvan。

編輯: 到更新上述(addedContacts和deletedContacts)方法的參數是已經存在的實體ID> 0

EDIT2: 按照您我建議移除用於插入新鮮的代碼(不存在於數據庫中)記錄來自更新方法。所以現在我的更新方法只會將現有的StandardContact記錄添加到RelayConfig集合中。但我仍然無法讓代碼正常工作。首先這裏是我使用的代碼

public void Update(RelayConfig relayConfig, List<StandardContact> addedContacts, List<StandardContact> deletedContacts) { 
     context.RelayConfigs.Add(relayConfig); 

     if (relayConfig.Id > 0) { 
      context.Entry(relayConfig).State = EntityState.Modified; 
     } 


     addedContacts.ForEach(contact => { 
      context.StandardContacts.Attach(contact); 
      relayConfig.StandardContacts.Add(contact); 
      objectContext.ObjectStateManager. 
       ChangeRelationshipState(relayConfig, contact, rs => rs.StandardContacts, EntityState.Added); 
     }); 
    } 

現在我只是專注於添加記錄。當StandardContact(聯繫變量)與任何其他現有RelayConfig對象沒有任何關係時,上面的代碼運行良好。在這種情況下,在聯接表中爲每個添加到RelayConfig.StandardContacts集合的聯繫人創建一個新條目。但是當StandardContact(聯繫變量)已經與其他RelayConfig對象有關係時,事情變得很難理解(不可預知的行爲)。在這種情況下,當StandardContact添加到RelayConfig.StandardContacts集合中時,StandardContact也會被添加到數據庫中,從而創建重複條目。不僅如此,還會創建一個新的RelayConfig對象(我不知道在哪裏)並將其插入到RelayConfigs表中。我實際上無法理解實體框架與多對多關係的工作方式。

@Ladislav,如果你有一些適用於多對多關係更新(對於分離的實體)的示例代碼,那麼我可以請求你請讓我看到同樣的內容。

問候, NIRVAN

EDIT3(解決方案):

最後,我結束了使用完全不同的方法。這裏是更新的代碼

public void Update(RelayConfig relayConfig, List<StandardContact> exposedContacts) { 

     context.Entry(relayConfig).State = EntityState.Modified; 

     relayConfig.StandardContacts.Clear(); 
     exposedContacts.ForEach(exposedContact => { 
      StandardContact exposedContactEntity = null; 
      exposedContactEntity = context.StandardContacts.SingleOrDefault(sc => sc.Id == exposedContact.Id); 
      if (exposedContactEntity != null) { 
       relayConfig.StandardContacts.Add(exposedContactEntity); 
      } 
     }); 
    } 

關於, Nirvan。

回答

10

問題是多對多關係有它自己的狀態。所以,如果你把這個:

addedContacts.ForEach(ad => relayConfig.StandardContacts.Add(ad)); 

你告訴EF,所有添加的聯繫人都將被插入到你的結表多對多的關係,建立新的關係,但調用此:

foreach (StandardContact standardContact in relayConfig.StandardContacts) { 
    if (standardContact.Id > 0) { 
     context.Entry(standardContact).State = EntityState.Modified; 
    } 
} 

將改變狀態的聯繫實體,但不是關係的狀態 - 它仍然被追蹤爲新的(順便說一句,它不能被修改,但只能添加,刪除或不改變)。因此,當您保存更改關係時,您的所有聯繫人都將添加到聯結表中,並且如果數據庫中已存在相同的關係,則您將得到異常(因爲聯結表僅包含兩個也是PK的FK,並且在這種情況下同樣的關係= PK違規)。

您還需要通過使用來設置關係狀態:

var objectContext = ((IObjectContextAdapter)context).ObjectContext; 
objectContext.ObjectStateManager.ChangeRelatioshipState(...); 

但這裏談到的問題:你必須剛剛創建與現有的或新的依賴的配置,也接觸了新的關係,現有的聯繫人之間的不同,其是完全新的 - 我建議你分別處理全新的聯繫人,否則你的代碼將非常複雜。

+0

感謝您的回覆。但我仍然有疑問。讓我們假設沒有addedRecords或deletedRecords。假設一個RelayConfig有兩個StandardContact記錄,並且我想將這些更改保存到這兩個StandardContact記錄中,除了將它們的EntityState更改爲已修改之外,是否還必須更改這兩個StandardContacts的RelationshipState?我的意思是我們如何更新關係的多方面(忽略現在添加和刪除的記錄)。 – Jatin 2012-04-23 11:10:04

+0

如果添加realayConfig,則必須將關係狀態更改爲未更改。如果你只是Attache realayConfig,你不必對這些關係做任何事情,但當你想添加新的關係時,附加可能會導致另一個問題。 – 2012-04-23 11:14:17

+0

請看我的編輯標記爲Edit2: – Jatin 2012-04-24 05:22:13