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

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

    其他解决方案

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

    结论

    我将结束这个假设与MSDN论坛上的另一个相关问题的单一链接。 检查Zeeshan Hirani的答案。 他是Entity Framework 4.0 Recipes的作者。 如果他说自动合并对象图不被支持,我相信他。

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

    编辑2:

    正如你可以看到这已经在2007年作为建议添加到MS Connect。MS已经关闭它作为在下一版本中要做的事情,但实际上没有做任何改进除了STE之外的差距。


    我有一个解决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的代码


    我知道这是OP的迟到,但由于这是一个非常常见的问题,我发布了这个以防万一它为别人服务。 我一直在讨论这个问题,我想我有一个相当简单的解决方案,我所做的是:

  • 通过将其状态设置为“修改”来保存主对象(例如博客)。
  • 在数据库中查询更新的对象,包括我需要更新的集合。
  • 查询并转换.ToList()我希望我的集合包含的实体。
  • 将主对象的集合更新到我从第3步获得的列表。
  • 保存更改();
  • 在下面的示例中,“dataobj”和“_categories”是由我的控制器接收的参数。“dataobj”是我的主对象,“_categories”是包含用户在视图中选择的类别ID的IEnumerable。

        db.Entry(dataobj).State = EntityState.Modified;
        db.SaveChanges();
        dataobj = db.ServiceTypes.Include(x => x.Categories).Single(x => x.Id == dataobj.Id);
        var it = _categories != null ? db.Categories.Where(x => _categories.Contains(x.Id)).ToList() : null;
        dataobj.Categories = it;
        db.SaveChanges();
    

    它甚至适用于多种关系

    链接地址: http://www.djcxy.com/p/34503.html

    上一篇: Update relationships when saving changes of EF4 POCO objects

    下一篇: Explanation of POCO