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

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

问题描述

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

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

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



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





在大多数情况下,我使用的是解决方案,直到它不产生任何错误,并至少保存了BlogPost的属性。关系会发生什么变化:通常使用新的PKs将标签添加到标签表中并且保存的BlogPost引用那些而不是原始的。当然,返回的标签有PK,在保存/更新方法之前,我检查PKs,它们等于数据库中的那些,所以EF认为它们是新的对象,那些PK是temp的。



我知道的一个问题,可能无法找到一个自动化的简单解决方案:当一个POCO对象的集合被更改时,应该由上述虚拟集合属性发生,因为那么FixupCollection技巧将在多对多关系的另一端更新反向引用。但是当一个View返回一个更新的BlogPost对象时,没有发生。这意味着也许没有简单的解决方案来解决我的问题,但这会让我很伤心,我会讨厌EF4-POCO-MVC的胜利:(同样这也意味着EF不能在MVC环境中做到这一点使用EF4对象类型:(我认为基于快照的更改跟踪应该会发现,更改后的BlogPost与现有PK的标签有关系。



Btw:我认为一对多的关系发生同样的问题(谷歌和我的同事这么说),我会在家里试试,但即使这样做不能帮助我在我的六对多关系中应用程序:(。

解决方案

我们来试试这样:




  • 将BlogPost附加到上下文。将对象附加到上下文对象的状态后,所有相关对象和所有关系都将设置为不变。

  • 使用context.ObjectStateManager。 ChangeObjectState将您的BlogPost设置为修改

  • 迭代gh标签收集

  • 使用context.ObjectStateManager.ChangeRelationshipState设置当前标签和BlogPost之间关系的状态。

  • SaveChanges



编辑:



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



问题背景



EF需求跟踪实体上的更改,以便持久性知道哪些记录必须更新,插入或删除。问题是ObjectContext负责跟踪更改。 ObjectContext能够跟踪附加实体的更改。



问题描述



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



解决方案



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




  • 快照 - 需要相同的上下文=无用于断开连接的场景

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

  • 手动同步。



单个实体上的手动同步很容易。您只需要附加实体并调用AddObject插入,DeleteObject进行删除或将ObjectStateManager中的状态设置为Modified以进行更新。当您必须处理对象图而不是单个实体时,真正的痛苦就会发生。当你必须处理独立协会(那些不使用国外关键财产)和许多关系时,这种痛苦就更糟了。在这种情况下,您必须手动同步对象图中的每个实体,而且还需要在对象图中的每一个关系。



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


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


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


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


此外,还有博客,这正好批评了EF的这种行为。



解决原因



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



其他提出的解决方案



而不是真正的合并功能EF团队提供了所谓的自我跟踪实体(STE),无法解决问题。首先,STE只有在整个处理中使用相同的实例时才有效。在Web应用程序中,除非将实例存储在视图状态或会话中,否则不是这样。由于我非常不高兴使用EF,我将检查NHibernate的功能。第一个观察指出,NHibernate可能有这样的功能



结论



我将以单一链接结束另一个< MSDN论坛上的href =http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/fdc5c7ac-dcf8-45a8-ab11-2ffbc338b0e5 =noreferrer>相关问题。检查Zeeshan Hirani的答案。他是 Entity Framework 4.0 Recipes 的作者。如果他说自动合并对象图不受支持,我相信他。



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



编辑2:



如您所见, MS Connect 作为2007年的建议.MS已经将其关闭,在下一个版本中要做,但实际上没有任何改善这一差距,除了STE。


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;

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 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.

"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.

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 :(.

解决方案

Let's try it this way:

  • Attach BlogPost to context. After attaching object to context the state of the object, all related objects and all relations is set to Unchanged.
  • Use context.ObjectStateManager.ChangeObjectState to set your BlogPost to Modified
  • Iterate through Tag collection
  • Use context.ObjectStateManager.ChangeRelationshipState to set state for relation between current Tag and BlogPost.
  • SaveChanges

Edit:

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:

Problem background

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.

Problem description

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).

Solution

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

  • Snapshot - requires same context = useless for disconnected scenario
  • Dynamic tracking proxies - requires same context = useless for disconnected scenario
  • Manual synchronization.

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.

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.

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.

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

Reason for solution

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.

Other proposed solution

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.

Conclusion

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.

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

Edit 2:

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天全站免登陆