ASP.NET MVC / EF4 / POCO / Repository - 如何更新关系?

时间:2011-01-11 03:11:26

标签: asp.net-mvc entity-framework-4 repository poco

我与评论建议之间存在1 .. *关系。

我的模型的相关部分(也是EF4映射的POCO):

public class Review
{
   public ICollection<Recommendations> Recommendations { get; set; }
}

修改视图上,我将推荐表示为一组复选框。

当我尝试添加新建议书作为编辑评论的一部分时(例如检查另一个框),什么也没发生 - 我知道为什么......

我使用“存根技术”来更新我的实体 - 例如我创建一个具有相同键的实体,将其附加到图表,然后执行ApplyCurrentValues。但这仅适用于标量属性,而不适用于导航属性。

我发现this StackOverflow question看起来不错,但我正在尝试研究如何使用POCO的/ Repository(以及ASP.NET MVC - 分离的上下文)。

由于我正在使用POCO,review.RecommendationsICollection<Recommendation>,所以我无法review.Recommendations.Attach。我也没有使用自我跟踪实体,所以我需要手动处理图形/更改跟踪 - 直到现在这还不是问题。

所以你可以看到这个场景:

修改:

  • 建议(ICollection<Recommendation>):
    • RecommendationOne(Recommendation
    • RecommendationTwo(Recommendation

如果我在编辑视图中,则已经选中了两个复选框。第三个(代表RecommendationThree)是未选中

但如果我检查该框,则上述模型变为:

修改:

  • 建议(ICollection<Recommendation>):
    • RecommendationOne(Recommendation
    • RecommendationTwo(Recommendation
    • RecommendationThree(Recommendation

所以我需要将RecommendationThree作为新实体添加到图表中。

我是否需要隐藏字段来比较现有实体的发布数据?或者我应该将实体存储在TempData中并将其与发布的实体进行比较吗?

修改

为避免混淆,这里是完整的应用程序堆栈调用:

ReviewController

[HttpPost]
public ActionResult Edit(Review review)
{
   _service.Update(review); // UserContentService
   _unitOfWork.Commit();
}

UserContentService

public void Update<TPost>(TPost post) where TPost : Post, new()
{
   _repository.Update(post); // GenericRepository<Post>
}

GenericRepository - 用作GenericRepository<Post>

public void Update<T2>(T2 entity) where T2 : class, new()
{
   // create stub entity based on entity key, attach to graph.

   // override scalar values
   CurrentContext.ApplyCurrentValues(CurrentEntitySet, entity);
}

因此,需要为每个推荐调用Update(或AddDelete)存储库方法,具体取决于它是新的/修改/删除的。

3 个答案:

答案 0 :(得分:8)

我已经接受了@jfar的答案,因为他让我走上了正确的道路,但我想我会在这里为其他人的利益添加一个答案。

关系没有得到更新的原因是由于以下原因:

1)完全断开连接的场景。 ASP.NET =无状态,新的上下文新增了每个HTTP请求。

2)由MVC(模型绑定)创建的编辑实体,但不存在于图形中。

3)当使用没有更改跟踪的POCO时,对实体执行.Attach会将其添加到图表中,但实体和任何子关系将不变。

4)我使用存根实体技巧和ApplyCurrentValues来更新实体,但这仅适用于标量属性,而不适用于导航属性。

所以 - 为了使上述功能得以实现,我必须明确设置对象的EntityState(由于ApplyCurrentValues而自动发生),以及导航属性。

并且存在问题 - 如果添加/修改/删除了导航属性,我如何知道?我没有可比较的对象 - 只有我认识的实体是“编辑过的”,但我不知道编辑了什么

所以解决方案到底是这样的:

[HttpPost]
public ActionResult Edit(Review review)
{
   var existingReview = _service.FindById(review.Id); // review is now in graph.
   TryUpdateModel(existingReview); // MVC equivalent of "ApplyCurrentValues" - but works for ALL properties - including navigationals
   _unitOfWork.Commit(); // save changed
}

就是这样。我甚至不需要我的_service.Update方法 - 因为我不再需要存根技巧 - 因为审核在图表中包含检索,ApplyCurrentValuesTryUpdateModel替换

现在当然 - 这不是并发性解决方案。

如果我加载了评论编辑视图,在我点击“提交”之前,其他人更改了评论,我的更改可能会丢失。

幸运的是,我有一个“最后赢”的并发模式,所以对我来说这不是问题。

我喜欢POCO,但是当你拥有无状态环境(MVC)和没有变化跟踪的组合时,他们是一种痛苦。

答案 1 :(得分:6)

使用分离的对象图是我最喜欢的EF缺点。只是痛苦的屁股。首先,你必须自己处理它。 EF不会帮助你。这意味着除了Review之外,您还必须发送有关已进行更改的一些信息。当您将Review附加到上下文时,它会将Review所有Recommendation所有关系设置为Unchanged州。 ApplyCurrentValues仅适用于您已经找到的标量值。因此,您必须使用有关更改的其他信息,并使用Added将关系状态设置为ObjectContext.ObjectStateManager.ChangeRelationshipState

我个人放弃了这种方法,我从DB加载对象图,首先将我的更改合并到附图并保存。

我回答了类似问题more deeply here

答案 2 :(得分:4)

也许我需要更多的背景但是错误的是:

recommendations.Add(newRecomendation)

回复评论:

好的,

错了
SomeServiceOrRepository.AddNewRecommendation( newRecommendation )

SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )

最后一句话?你的意思是两个问题?

这应该不难。

总结一下我的答案,我认为你正在“艰难地”做事,而且应该专注于发布与你想要完成的CRUD行动相对应的表格价值。

如果新实体可以与您编辑的实体同时进入,那么您应该以不同的方式为它们添加前缀,以便模型绑定器能够接收它。即使您有多个新项目,也可以使用相同的[0]语法,只需在“name”字段前加上New或其他内容。

在这种情况下,很多时候您不能依赖实体框架图功能,因为从集合中删除实体绝不意味着它应该设置为删除。

如果表单是不可变的,你也可以尝试使用ObjectSet的generized attach函数:

theContect.ObjectSet<Review>().Attach( review )

远离这种情况的方法很多。也许您可以发布您的控制器并查看代码?