2012-05-28 17 views
3

我正在使用实体框架和DbContext API来构建我的应用程序,但我无法处理具有多对多关系的对象。简化保存法可能看起来像这样使用DbContext API添加和删除多对多API

public void MyObj_Save(MyObj myobj) 
{ 
    DbContext.Entry(myobj).State = EntityState.Added; 
    DbContext.SaveChanges(); 
} 

此代码工作正常,但如果MyObj中包含了许多一对多的关系,这是不会被保存。我使用旧的POCO API,我需要附上相关对象上下文知道,但我不能找到一种方法,用的DbContext API正确地做到这一点 - 以下

public void MyObj_Save(MyObj myobj, List<OtherObj> otherObjList) 
{ 
    foreach (OtherObj otherObj in otherObjList) 
    { 
    DbContext.OtherObj.Attach(otherObj); 
    myobj.OtherObj.Add(otherObj); 
    } 

    DbContext.Entry(myobj).State = EntityState.Added; 
    DbContext.SaveChanges(); 
} 

一个简单的例子我没有得到任何错误,但关系不会被保存。该怎么办?

+0

它应该工作。是否保存了myobj?但连接表中没有新行? – Slauma

+0

通过将实体附加到上下文中,您不确定要实现什么目标。如果你附加,那么它“向ObjectContext添加一个对象,并将该对象设置为Unchanged状态。在Unchanged状态下,实体框架将实体键值视为final。如果多于一个特定类型的实体具有相同的键值,实体框架将抛出​​一个异常。为避免发生异常,请使用AddObject方法附加分离的对象,然后相应地更改状态。请参阅MSDN [文章](http://msdn.microsoft.com/en-us/library/bb896271.aspx)。 –

+0

@EmmieGabrielleLewis:他想创建** new **对象'myobj'和**现有**对象'otherObjList'的列表之间的关系。使用'Attach'对此是正确的。 – Slauma

回答

3

我引用您的(重要!)评论:我送的方法

的对象连接和EntityState是 不变。我的DbContext的配置是,我有禁用 AutoDetectChangesEnabled ...

所以,你的代码应该是这样的:

DbContext.Configuration.AutoDetectChangesEnabled = false; 

DbContext.Entry(myobj).State = EntityState.Unchanged; 
foreach (OtherObj otherObj in otherObjList) 
    DbContext.Entry(otherObj).State = EntityState.Unchanged; 

// entering MyObj_Save method here 
foreach (OtherObj otherObj in otherObjList) 
{ 
    //DbContext.OtherObj.Attach(otherObj); // does not have an effect 
    myobj.OtherObj.Add(otherObj); 
} 

DbContext.Entry(myobj).State = EntityState.Added; 
DbContext.SaveChanges(); 

这确实不起作用,因为没有按EF请注意,您已更改myobjmyobj.OtherObj.Add(otherObj);行中的OtherObj列表之间的关系,因为您已禁用自动更改检测。所以,没有条目会写入连接表。只有myobj本身将被保存。

您不能在实体上设置任何状态,以将状态管理器置于关系被保存的状态,因为它不是一个实体状态,在这里它很重要,而是关系状态。这些是对象状态管理器中的单独条目,它们是通过更改检测创建和维护的。

我看到三个解决方案:

  • DbContext.Configuration.AutoDetectChangesEnabled = true;

  • 呼叫DetectChanges手动:

    //... 
    DbContext.Entry(myobj).State = EntityState.Added; 
    DbContext.ChangeTracker.DetectChanges(); 
    DbContext.SaveChanges(); 
    
  • 拆离新myobj从你设置成Added状态之前的上下文(这对我来说感觉很不好):

    // entering MyObj_Save method here 
    DbContext.Entry(myobj).State = EntityState.Detached; 
    foreach (OtherObj otherObj in otherObjList) 
    //... 
    

也许是可能的 - 通过将IObjectContextAdapter获取到ObjectContext - 手动修改对象状态管理的关系项,但我不知道怎么办。

在我看来,这个手动操作实体(和关系)状态的过程并不是你应该使用EF的方式。 AutoDetectChangesEnabled已被引入使EF的工作更加简单和安全,并且唯一推荐的禁用它的情况是高性能要求(例如对于批量插入)。如果您在不需要自动更改检测功能时遇到难以检测到的问题,并且需要EF内部工作的高级知识才能修复这些错误。

+0

来吧,我的所有尊重 - 我知道为什么它不起作用,但我找不到一个不会影响架构决策或可能导致其他缺陷的解决方案。确切地说要保存哪些对象或不保存。我不是在寻找简单的解决方案,并且自动检测更改可以轻松地在大型应用程序中提供问题。无论如何 - 这种幻想让我们彼此之间更加接近理解,所以让我们在这里结束,而且我也找到了解决方案,我将在下面发布答案。 – keysersoze

+0

@keysersoze:我的答案中包含了您自己的答案(至少提到过)作为选项。但是:我真的不喜欢它! (但很好知道它是如何工作:)) – Slauma

2
public void MyObj_Save(MyObj myobj, List<OtherObj> otherObjList) 
{ 
    DbContext.Entry(myobj).State = EntityState.Added; 

    foreach (OtherObj otherObj in otherObjList) 
    { 
     (((System.Data.Entity.Infrastructure.IObjectContextAdapter)DbContext) 
      .ObjectContext) 
      .ObjectStateManager 
      .ChangeRelationshipState(myobj, otherObj, 
       q => q.OtherObjs, EntityState.Added); 
    } 

    DbContext.SaveChanges(); 
} 

再一次,它是一个简化而不是真实的生活实例!

+0

我重新格式化了一下代码(换行符),因为它值得轻松阅读! – Slauma