保存 EF4 POCO 对象更改时更新关系 [英] Update relationships when saving changes of EF4 POCO objects

查看:22
本文介绍了保存 EF4 POCO 对象更改时更新关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Entity Framework 4, POCO objects and ASP.Net MVC2. I have a many to many relationship, lets say between BlogPost and Tag entities. This means that in my T4 generated POCO BlogPost class I have:

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

我从 ObjectContext 的一个实例中请求一个 BlogPost 和相关的标签,并将其发送到另一个层(MVC 应用程序中的视图).后来我取回更新后的博客帖子,其中更改了属性和更改了关系.例如,它有标签A"、B"和C",而新标签是C"和D".在我的特定示例中,没有新标签并且标签的属性永远不会改变,因此唯一应该保存的是已更改的关系.现在我需要将它保存在另一个 ObjectContext 中.(更新:现在我尝试在同一个上下文实例中执行但也失败了.)

I ask for a BlogPost and the related Tags from an instance of the ObjectContext and send it to another layer (View in the MVC application). Later I get back the updated BlogPost with changed properties and changed relationships. For example it had tags "A" "B" and "C", and the new tags are "C" and "D". In my particular example there are no new Tags and the properties of the Tags never change, so the only thing which should be saved is the changed relationships. Now I need to save this in another ObjectContext. (Update: Now I tried to do in the same context instance and also failed.)

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

The problem: I can't make it save the relationships properly. I tried everything I found:

  • Controller.UpdateModel 和 Controller.TryUpdateModel 不起作用.
  • 从上下文中获取旧的 BlogPost 然后修改集合不起作用.(与下一点不同的方法)
  • 可能会起作用,但我希望这只是一种解决方法,而不是解决方案:(.
  • 在各种可能的组合中为博客帖子和/或标签尝试了附加/添加/更改对象状态函数.失败.
  • 这个看起来像我需要,但它不起作用(我试图修复它,但不能解决我的问题).
  • 尝试了 ChangeState/Add/Attach/... 上下文的关系对象.失败.
  • Controller.UpdateModel and Controller.TryUpdateModel don't work.
  • Getting the old BlogPost from the context then modifying the collection doesn't work. (with different methods from the next point)
  • This probably would work, but I hope this is just a workaround, not the solution :(.
  • Tried Attach/Add/ChangeObjectState functions for BlogPost and/or Tags in every possible combinations. Failed.
  • This looks like what I need, but it doesn't work (I tried to fix it, but can't for my problem).
  • Tried ChangeState/Add/Attach/... the relationship objects of the context. Failed.

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

"Doesn't work" means in most cases that I worked on the given "solution" until it produces no errors and saves at least the properties of BlogPost. What happens with the relationships varies: usually Tags are added again to the Tag table with new PKs and the saved BlogPost references those and not the original ones. Of course the returned Tags have PKs, and before the save/update methods I check the PKs and they are equal to the ones in the database so probably EF thinks that they are new objects and those PKs are the temp ones.

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

A problem I know about and might make it impossible to find an automated simple solution: When a POCO object's collection is changed, that should happen by the above mentioned virtual collection property, because then the FixupCollection trick will update the reverse references on the other end of the many-to-many relationship. However when a View "returns" an updated BlogPost object, that didn't happen. This means that maybe there is no simple solution to my problem, but that would make me very sad and I would hate the EF4-POCO-MVC triumph :(. Also that would mean that EF can't do this in the MVC environment whichever EF4 object types are used :(. I think the snapshot based change tracking should find out that the changed BlogPost has relationships to Tags with existing PKs.

顺便说一句:我认为一对多关系也会出现同样的问题(谷歌和我的同事是这么说的).我会在家里试一试,但即使这对我的应用程序中的六个多对多关系没有帮助:(.

Btw: I think the same problem happens with one-to-many relations (google and my colleague say so). I will give it a try at home, but even if that works that doesn't help me in my six many-to-many relationships in my app :(.

推荐答案

让我们这样试试:

  • 将博客帖子附加到上下文中.将对象附加到上下文对象的状态后,所有相关对象和所有关系都设置为未更改.
  • 使用 context.ObjectStateManager.ChangeObjectState 将您的博客帖子设置为已修改
  • 遍历标签集合
  • 使用 context.ObjectStateManager.ChangeRelationshipState 设置当前 Tag 和 BlogPost 之间关系的状态.
  • 保存更改

我想我的一个评论给了您错误的希望,即 EF 会为您进行合并.我在这个问题上玩了很多,我的结论是 EF 不会为你做这个.我想你也在 MSDN.事实上,互联网上有很多这样的问题.问题是没有明确说明如何处理这种情况.那么让我们来看看这个问题:

I guess one of my comments gave you false hope that EF will do the merge for you. I played a lot with this problem and my conclusion says EF will not do this for you. I think you have also found my question on MSDN. In reality there is plenty of such questions on the Internet. The problem is that it is not clearly stated how to deal with this scenario. So lets have a look on the problem:

问题背景

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

EF needs to track changes on entities so that persistance knows which records have to be updated, inserted or deleted. The problem is that it is ObjectContext responsibility to track changes. ObjectContext is able to track changes only for attached entities. Entities which are created outside the ObjectContext are not tracked at all.

问题描述

基于上面的描述,我们可以清楚地表明 EF 更适合于实体始终附加到上下文的连接场景 - 典型的 WinForm 应用程序.Web 应用程序需要断开连接的场景,其中上下文在请求处理后关闭,实体内容作为 HTTP 响应传递给客户端.下一个 HTTP 请求提供必须重新创建、附加到新上下文并持久化的实体的修改内容.娱乐通常发生在上下文范围之外(具有持久性无知的分层架构).

Based on above description we can clearly state that EF is more suitable for connected scenarios where entity is always attached to context - typical for WinForm application. Web applications requires disconnected scenario where context is closed after request processing and entity content is passed as HTTP response to the client. Next HTTP request provides modified content of the entity which has to be recreated, attached to new context and persisted. Recreation usually happends outside of the context scope (layered architecture with persistance ignorace).

解决方案

那么如何处理这种断开连接的情况呢?使用 POCO 类时,我们有 3 种方法来处理更改跟踪:

So how to deal with such disconnected scenario? When using POCO classes we have 3 ways to deal with change tracking:

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

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

Manual synchronization on single entity is easy task. You just need to attach entity and call AddObject for inserting, DeleteObject for deleting or set state in ObjectStateManager to Modified for updating. The real pain comes when you have to deal with object graph instead of single entity. This pain is even worse when you have to deal with independent associations (those that don't use Foreign Key property) and many to many relations. In that case you have to manually synchronize each entity in object graph but also each relation in object graph.

MSDN 文档提出手动同步作为解决方案:附加和分离对象 说:

Manual synchronization is proposed as solution by MSDN documentation: Attaching and Detaching objects says:

对象附加到对象上处于未更改状态的上下文.如果你需要改变对象的状态或关系,因为你知道你的对象被修改分离状态,使用其中之一以下方法.

Objects are attached to the object context in an Unchanged state. If you need to change the state of an object or the relationship because you know that your object was modified in detached state, use one of the following methods.

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

Mentioned methods are ChangeObjectState and ChangeRelationshipState of ObjectStateManager = manual change tracking. Similar proposal is in other MSDN documentation article: Defining and Managing Relationships says:

如果您正在使用断开连接的您必须手动管理对象同步.

If you are working with disconnected objects you must manually manage the synchronization.

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

Moreover there is blog post related to EF v1 which criticise exactly this behavior of EF.

解决原因

EF 有许多有用"的操作和设置,例如 Refresh加载ApplyCurrentValues, ApplyOriginalValuesMergeOption 等.但根据我的调查,所有这些功能仅适用于单个实体并且仅影响标量属性(= 不是导航属性和关系).我宁愿不使用嵌套在实体中的复杂类型来测试此方法.

EF has many "helpful" operations and settings like Refresh, Load, ApplyCurrentValues, ApplyOriginalValues, MergeOption etc. But by my investigation all these features work only for single entity and affects only scalar preperties (= not navigation properties and relations). I rather not test this methods with complex types nested in entity.

其他建议的解决方案

EF 团队提供了一种叫做Self Tracking Entities (STE) 不能解决问题.首先,仅当整个处理使用相同的实例时,STE 才有效.在 Web 应用程序中,除非您将实例存储在视图状态或会话中,否则情况并非如此.因此,我对使用 EF 非常不满意,我将检查 NHibernate 的功能.第一个观察表明 NHibernate 可能具有这样的功能.

Instead of real Merge functionality EF team provides something called Self Tracking Entities (STE) which don't solve the problem. First of all STE works only if same instance is used for whole processing. In web application it is not the case unless you store instance in view state or session. Due to that I'm very unhappy from using EF and I'm going to check features of NHibernate. First observation says that NHibernate perhaps has such functionality.

结论

我将通过指向另一个 相关问题.检查 Zeeshan Hirani 的回答.他是Entity Framework 4.0 Recipes 的作者.如果他说不支持对象图的自动合并,我相信他.

I will end up this assumptions with single link to another related question on MSDN forum. Check Zeeshan Hirani's answer. He is author of Entity Framework 4.0 Recipes. If he says that automatic merge of object graphs is not supported, I believe him.

但仍有可能我完全错了,EF 中存在一些自动合并功能.

But still there is possibility that I'm completely wrong and some automatic merge functionality exists in EF.

编辑 2:

如您所见,这已添加到 MS Connect 作为 2007 年的建议.MS 已经关闭它作为下一个版本要做的事情,但实际上除了 STE 之外没有采取任何措施来改善这个差距.

As you can see this was already added to MS Connect as suggestion in 2007. MS has closed it as something to be done in next version but actually nothing had been done to improve this gap except STE.

这篇关于保存 EF4 POCO 对象更改时更新关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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