2012-04-23 143 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