在保存EF4 POCO对象的更改时更新关系?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (18)

实体框架4,POCO对象和ASP.Net MVC2。我有一个多对多的关系,可以说BlogPost和Tag实体之间。这意味着在我生成的POCO BlogPost类中我有:

public virtual ICollection<Tag> Tags {
    // getter and setter with the magic FixupCollection
}
private ICollection<Tag> _tags;

我从ObjectContext的一个实例请求一个BlogPost和相关的标签,并将其发送到另一个层(在MVC应用程序中查看)。稍后我会更新更新的BlogPost,并更改属性并更改关系。例如它有标签“A”“B”和“C”,新标签是“C”和“D”。在我的特殊例子中,没有新的标签,标签的属性从不改变,所以唯一应该保存的是改变的关系。现在我需要将它保存在另一个ObjectContext中。(更新:现在我试图在同一个上下文实例中执行,但也失败了。)

问题是:我无法让它正确保存关系。我尝试了我发现的一切:

  • Controller.UpdateModel和Controller.TryUpdateModel不起作用。
  • 从上下文获取旧的BlogPost,然后修改集合不起作用。(用下一点的不同方法)
  • 尝试每种可能的组合中的BlogPost和/或标签的Attach / Add / ChangeObjectState函数。失败。
  • 尝试ChangeState / Add / Attach / ...上下文的关系对象。失败。

“不工作”在大多数情况下意味着我在给定的“解决方案”上工作,直到它不产生错误并至少保存BlogPost的属性。这些关系会发生什么变化:通常,标签会以新的PK添加到标签表中,并且已保存的BlogPost会引用这些标签,而不是原始标签。当然,返回的标签有PK,在保存/更新方法之前,我检查PK,它们与数据库中的相同,因此EF可能认为它们是新对象,而这些PK是临时的。

我知道的一个问题,可能使它找不到一个自动化的简单解决方案:当一个POCO对象的集合发生变化时,应该通过上面提到的虚拟集合属性来实现,因为然后FixupCollection技巧会更新另一端的反向引用的多对多关系。然而,当一个View“返回”一个更新的BlogPost对象时,并没有发生。这意味着可能没有简单的解决方案来解决我的问题,但这会让我非常难过,而且我会讨厌EF4-POCO-MVC的胜利:(也就是说EF在MVC环境中无法做到这一点使用EF4对象类型:(我认为基于快照的更改跟踪应该发现更改后的BlogPost与具有现有PK的标记具有关系。

提问于
用户回答回答于

让我们试试这样:

  • 将BlogPost附加到上下文。将对象附加到上下文后,对象的状态,所有相关对象和所有关系都设置为“未更改”。
  • 使用context.ObjectStateManager.ChangeObjectState将您的BlogPost设置为Modified
  • 迭代Tag收集
  • 使用context.ObjectStateManager.ChangeRelationshipState设置当前标签和BlogPost之间关系的状态。
  • 保存更改

编辑:

我想我的一个评论给了你一个虚假的希望,即EF会为你合并。我在这个问题上玩了很多,我的结论是EF不会为你做这件事。我想你也在MSDN上发现了我的问题。事实上,互联网上有很多这样的问题。问题在于没有清楚说明如何处理这种情况。所以让我们看看这个问题:

问题背景

EF需要跟踪实体的变化,以便持久性知道哪些记录必须更新,插入或删除。问题是跟踪更改是ObjectContext的责任。ObjectContext能够仅追踪附加实体的更改。在ObjectContext之外创建的实体根本不被跟踪。

问题描述

基于上面的描述,我们可以清楚地说明EF更适合连接场景,其中实体总是附加到上下文 - 典型的WinForm应用程序。Web应用程序需要断开连接的场景,在请求处理和实体内容作为HTTP响应传递给客户端后,关闭上下文。接下来的HTTP请求提供了必须重新创建的实体的修改内容,附加到新的上下文并保持。休闲活动通常在上下文范围之外发生(分层架构,坚持不懈)。

那么如何处理这种不连贯的情况呢?在使用POCO课程时,我们有3种方法来处理变更跟踪:

  • 快照 - 需要相同的上下文=断开连接的情况下无用
  • 动态跟踪代理 - 需要相同的上下文=对断开连接的情况无用
  • 手动同步。

单个实体的手动同步是一件容易的事情。您只需附加实体并调用AddObject进行插入,DeleteObject用于删除或将ObjectStateManager中的状态设置为Modified以进行更新。当你不得不处理对象图而不是单个实体时,真正的痛苦来临了。当你必须处理独立的关联(那些不使用外键属性的关联)和多对多关系时,这种痛苦更加严重。在这种情况下,您必须手动同步对象图中的每个实体,而且也要手动同步对象图中的每个关系。

MSDN文档提出了手动同步的解决方案:附加和分离对象表示:

对象以Unchanged状态附加到对象上下文。如果您需要更改对象或关系的状态,因为您知道对象已在分离状态下被修改,请使用以下方法之一。

提及的方法是ChangeObjectState和ObjectStateManager的ChangeRelationshipState =手动更改跟踪。在其他MSDN文档中也有类似的建议:定义和管理关系

如果您正在使用断开的对象,则必须手动管理同步。

此外,还有与EF v1有关的博客文章,它批评了EF的这种行为。

解决问题的理由

EF有许多“有用的”操作和设置,如刷新加载ApplyCurrentValuesApplyOriginalValuesMergeOption等。但通过我的调查,所有这些功能仅适用于单个实体,并且只影响标量属性(=不导航属性和关系)。我宁愿不用嵌套在实体中的复杂类型来测试这些方法。

其他解决方案

EF团队不提供真正的合并功能,而是提供一些称为自我跟踪实体(STE)的东西,这些东西不能解决问题。首先,只有在同一实例用于整个处理的情况下,STE才有效。在Web应用程序中,除非将实例存储在视图状态或会话中,否则情况并非如此。由于我使用EF非常不满意,我将检查NHibernate的功能。首先观察说NHibernate可能具有这样的功能

但是我仍然有可能完全错误,EF中存在一些自动合并功能。

用户回答回答于

我有一个解决Ladislav上面描述的问题的办法。我为DbContext创建了一个扩展方法,该方法将根据提供的图形和持久图形的差异自动执行添加/更新/删除操作。

目前使用实体框架,您需要手动执行联系人的更新,检查每个联系人是否是新的并添加,检查是否更新和编辑,检查是否删除,然后将其从数据库中删除。一旦你必须为一个大系统中的几个不同的聚集做这个,你就会意识到必须有一个更好,更通用的方法。

请看看它是否可以帮助http://refactorthis.wordpress.com/2012/12/11/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-图的的独立式实体/

你可以直接去https://github.com/refactorthis/GraphDiff的代码

扫码关注云+社区