违反了多重约束。关系'...'的作用'...'具有多重性1或0..1

时间:2013-04-08 17:46:43

标签: asp.net entity-framework

我从DbContext收到以下错误:“违反了多重约束。关系'MyModel.FK_ChildEntities_MyEntities'的角色'MyEntity'具有多重性1或0..1。”

使用ASP.NET,Entity Framework 4

使用分离的实体

第二次尝试将实体重新附加到dbcontext时发生错误。该方案是一次失败的保存,然后是重新尝试。

我在会话中有一个分离的实体。用户更改表单中的属性,添加内容,删除内容,最后单击保存。我从dbcontext的新实例获取实体的附加副本,将更改从分离实体应用到附加实体,验证,查找错误并中止。用户更改任何内容并再次保存。

在第二次保存时,整个保存过程重复进行,只有这一次一切都变成了地狱。几乎所有东西都是重复的,导致一个错误或另一个或全部错误。视图和查找表中仅应该是引用的值将被创建为新的并重新分配的id。我已经能够解决的大多数问题,但我留下了多重性错误。子元素被创建为其他子元素的精确副本,直到唯一ID,仅在已添加状态。或者,如果我引用某些属性,而不是克隆未修改的子项,则会删除新的属性。无论哪种方式,没有任何代码像第一次执行那样执行。

我正在丢弃每个保存尝试的dbcontext和附加实体的实例。我认为这足以恢复任何变化,但必须坚持下去。唯一没有丢弃或重置的是分离的实体,它正处于会话中,但我没有对它进行任何更改。至少不是直接的。

代码(非常简化)是这样的:

void Save()
{
using (var context = new MyContext())
{
   // detached entity from session
   MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];

   // attached entity from context
   MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);       


   // <remove children representing lookup table elements from detachedEntity to prevent duplicates>
   // <remove children representing view elements from detachedEntity to prevent duplicates>


   // <apply changes from detachedEntity to attachedEntity>


   // <add new children>
   // <remove deleted children>
   // <update modified children>


   // <set entity state to unchanged on view and lookup elements of attachedEntity to ensure no duplicates...>


   // <validate>


   if (errors.count>0)
     // <report errors>
   else
     context.SaveChanges();
}
}

作为示例,这会产生多重性错误:

// represents first save:
    using (var context = new MyContext())
    {
       // detached entity from session
       MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];

       // attached entity from context
       MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);       

       int debug1 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug1 == 0;

       attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());

       int debug2 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug2 == 1;

    }

// represents second save:
    using (var context = new MyContext())
    {
       // detached entity from session
       MyEntity detachedEntity = (MyEntity)Session["DetachedEntity"];

       // attached entity from context
       MyEntity attachedEntity = context.MyEntities.Single(x=>x.id == detachedEntity.id);    

       int debug1 = context.ChangeTracker.Entries<ChildEntity>().Count(); // debug1 == 0;

       attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());

       int debug2 = context.ChangeTracker.Entries<ChildEntity>().Count(); // multiplicity error;

    }

8 个答案:

答案 0 :(得分:15)

以某种方式dbcontext会记住添加了哪些对象。如果完全相同的对象出现两次,那么...打击

而不是将我的分离实体中的子实体添加到附加的实体,我应该创建每个孩子的新副本

ChildEntity detachedChild = detachedEntity.ChildEntities.First();
attachedEntity.ChildEntities.Add(new ChildEntity { 
   propertyA = detachedChild.propertyA,
   propertyB = detachedChild.propertyB
});

而不是

attachedEntity.ChildEntities.Add(detachedEntity.ChildEntities.First());

答案 1 :(得分:7)

问题是应该为detaachedChild.parent分配attachParent。

foreach(var detachedEntity in detachedEntities)
{
     attachedEntity.ChildEntities.Add(detachedEntity); 
     detachedEntity.ParentEntity = attachedEntity;
}

答案 2 :(得分:2)

您要尝试执行的操作类似于:

ChildEntity childEntity = new ChildEntity()
{
  //do mapping or provide data EXCEPt THE PRIMARY KEY
}

foreach(ParentEntity parentEntity in parentEntities)
{
    parentEntity.Add(childEntity);
}

_dbContext.SaveChanges();

结果

Multiplicity constraint violated. The role '…' of the relationship '…' has multiplicity 1 or 0..1

错误消息的原因是

每次_dbContext将childEntity添加到某个parentEntity时,它都会将生成的主键设置为childEntity,因此在foreach的第二个循环中,主键将被复制

解决方法-方法1-适用于简单情况

foreach(ParentEntity parentEntity in parentEntities)
{
    //Make a new object every time
    ChildEntity childEntity = new ChildEntity()
    {
      //do mapping or provide data EXCEPt THE PRIMARY KEY
    }

    parentEntity.Add(childEntity);
}

_dbContext.SaveChanges();

解决方法-方法2-适用于复杂情况

using YOUR_PROJECT.ANY_FOLDER.DeepCopyExtensions;    

ChildEntity childEntity = new ChildEntity()
{
  //do mapping or provide data EXCEPt THE PRIMARY KEY
}

foreach(ParentEntity parentEntity in parentEntities)
{
    //makes a copy of the childEntity object and pass it to the _dbContext, after saving each copy will be separated and the original object childEntity wont be touched 
    parentEntity.Add(DeepCopyByExpressionTrees.DeepCopyByExpressionTree(childEntity));
}

_dbContext.SaveChanges();

“ DeepCopyByExpressionTrees.DeepCopyByExpressionTree(childEntity)”是什么方法?

检查此项目here,下载源代码,并且仅将类文件“ DeepCopyByExpressionTrees.cs”作为帮助程序类包含到您的项目中,并在任何地方开始使用。

谢谢

答案 3 :(得分:1)

确保检查您要添加的对象的属性。在我的情况下,它错误地引用了它不喜欢的每个添加上的相同无效对象,因此抛出了你在这里的相同错误。

答案 4 :(得分:0)

EF 6更新

对于我来说,将对象状态设置为添加声音逻辑也是

ChildEntity detachedChild = detachedEntity.ChildEntities.First();
var newChild = new ChildEntity { 
   propertyA = detachedChild.propertyA,
   propertyB = detachedChild.propertyB
});

// Mark all added entity entity state to Added
 attachedEntity.ChildEntities.Add(newChild );
                        db.Entry(newChild ).State = EntityState.Added;

http://www.entityframeworktutorial.net/EntityFramework4.3/update-one-to-many-entity-using-dbcontext.aspx

答案 5 :(得分:0)

当我有未设置的导航属性或属于错误的Code First DBContext的导航属性时,我遇到此错误

答案 6 :(得分:0)

我通过将父实体中的子集合虚拟化来解决此问题。这样一来,在大多数情况下,子实体不更改时,即可轻松更新实体。

答案 7 :(得分:0)

我遇到了类似的问题,但是我的查询后是由AsNoTracking()引起的。

我有这样的东西

var myObject = dbContext.GetRepo<myType>().Query().AsNoTracking().SingleOrDefault()

然后稍后我使用该对象设置另一个对象。

var myChild = new Child { parent = myObect }

很显然,EntityFramework试图创建一个全新的对象,因此会导致多重错误。