实体框架更新多对多关系 - POCO [英] Entity Framework Updating Many-To-Many Relationships - POCO

查看:115
本文介绍了实体框架更新多对多关系 - POCO的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个实体之间的多对多关系 RelayConfig StandardContact



EntityBase,IDataErrorInfo {
...
/ / Associations
public virtual ICollection< StandardContact>标准接触{get;组; }
}


public class StandardContact:EntityBase,IDataErrorInfo {
...
//关联
public virtual ICollection< RelayConfig> RelayConfigs {get;组; }
}

现在我试图更新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 
消息=违反PRIMARY KEY约束'PK__Standard__EE33D91D1A14E395'。无法在对象'dbo.StandardContactRelayConfigs'中插入重复键。

dbo.StandardContactRelayConfigs是链接RelayConfig和StandardContact的链接表。如您所见,如果Id> 0(除了在更新方法结束时设置的删除记录除外),更新代码会将所有实体更改为修改状态。



我真的不明白为什么实体框架试图在链接表中插入行,并且与上述异常失败。我已经将现有的RelayConfig.StandardContacts实体的EntityState更改为Modified。



简而言之,为什么我会收到上面列出的例外。



regards,
Nirvan。 / p>

编辑:
上面的Update方法的参数(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 );
objectContext.ObjectStateManager
ChangeRelationshipState(relayConfig,contact,rs => rs.StandardContacts,EntityState.Added);
});
}

现在我只是专注于增加的记录。当StandardContact(联系变量)与任何其他现有的RelayConfig对象没有任何关系时,上述代码工作正常。在这种情况下,会在连接表中为每个添加到RelayConfig.StandardContacts集合的联系人创建一个新条目。但是当StandardContact(联系变量)已经与其他RelayConfig对象关系时,事情会变得丑陋(不可预知的行为)。在这种情况下,当将StandardContact添加到RelayConfig.StandardContacts集合中时,StandardContact也会添加到数据库中,从而创建重复的条目。不仅如此,还会创建一个新的RelayConfig对象(我不知道从哪里)并插入到RelayConfigs表中。我真的不能理解实体框架与多对多关系的工作方式。



@Ladislav,如果您有一些适用于多对多关系更新的示例代码(对于分离的实体),那么我可以请求您显示相同的



regards,
Nirvan



Edit3(Solution):



最终我最终使用完全不同的方法。以下是更新的代码

  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);
}
});
}

regards,
Nirvan。

解决方案

问题是多对多关系有自己的状态。所以如果你这样称呼:

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

你告诉EF,所有添加的联系人都是新的关系,将被插入到你的交叉表中许多很多关系,但是调用这个:

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

将更改联系实体的状态,但不会更改状态的关系 - 它仍然被追踪为新的(btw,它不能修改,但只是添加,删除或不变)。所以当保存所有联系人的更改关系时,都会添加到联结表中,如果数据库中已经存在相同的关系,则会收到异常(因为连接表只包含两个也是PK的FK,在这种情况下,相同的关系= PK违例)。



您还需要使用以下方式设置关系状态:

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

但是这里出现了问题:您现在的联系人之间必须有所不同,新的依赖配置和全新的联系人 - 我建议您单独处理全新的联系人,否则您的代码将非常复杂。


I have following Many-To-Many relationship between two entities RelayConfig and StandardContact

Entities:

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; }
}

Now I am trying to update RelayConfig and its relations with the StandardContact. Here is the code that updates 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;
            }
        });
    }
    ...
}

When I run the update, I am getting exception, whose inner exception is given below.

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

dbo.StandardContactRelayConfigs is the linking table that links RelayConfig and StandardContact. As you can see, the Update code changes all the entities to modified state if Id > 0 (except deleted records that are set in the end of the Update method).

I really cannot understand why entity framework is trying to insert row in the linked table and failing with the above exception. I already change the EntityState of existing RelayConfig.StandardContacts entities to Modified.

In short why do I get the exception pasted above.

regards, Nirvan.

Edit: The parameters to Update method above (addedContacts and deletedContacts) are already existing entities with Id > 0.

Edit2: As per your suggestions I removed the code for inserting fresh (not existing in database) records from the update method. So now my update method only adds existing StandardContact records to RelayConfig collection. But I still cannot get the code to work properly. First here is the code that I am using

    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);
        });
    }

For now I am just concentrating on added records. The above code works well when the StandardContact (contact variable) does not have any relationships with any other existing RelayConfig objects. In that case, a new entry is created in the junction table for each contact added to the RelayConfig.StandardContacts collection. But things get ugly (unpredictable behaviour) when the StandardContact (contact variable) is already in relationship with other RelayConfig objects. In that case, when the StandardContact is added to RelayConfig.StandardContacts Collection, the StandardContact also gets added in the database thus creating duplicate entry. Not only that, a new RelayConfig object is also created (I don't know from where) and inserted to the RelayConfigs table. I am really not able to comprehend the way entity framework works with Many-To-Many relationships.

@Ladislav, if you have some sample code that works on Many-To-Many relationship updates (for detached entities) then can I request you to please show me the same.

regards, Nirvan

Edit3 (Solution):

Eventually I ended up using a completely different approach. Here is the code for the Update

    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);
            }
        });
    }

regards, Nirvan.

解决方案

The problem is that many-to-many relation has its own state. So if you call this:

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

You tell EF that all added contacts are new relations which will be inserted to your junction table for many to many relation but calling this:

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

will change state of the contact entity but not the state of the relation - it is still tracked as new (btw. it cannot be modified but only added, deleted or unchanged). So when you save changes relations for all your contacts are added to junction table and if the same relation already exists in the database you will get exception (because junction table contains only two FKs which are also PK and in such case same relation = PK violation).

You also need to set state for relations by using:

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

But here comes the problem: you must differ between existing contacts which just created a new relation with existing or new relying config and also contacts which are completely new - I suggest you to handle completely new contacts separately otherwise your code will be very complex.

这篇关于实体框架更新多对多关系 - POCO的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆