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

RPM*_*984 13 asp.net-mvc repository poco entity-framework-4

我在ReviewRecommendations之间有一个*..*的关系.

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

public class Review
{
   public ICollection<Recommendations> Recommendations { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

编辑视图中,我将建议书表示为一组复选框.

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

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

我发现这个StackOverflow问题看起来不错,但我正在尝试弄清楚如何使用POCO的/ Repository(以及ASP.NET MVC - 分离的上下文).

由于我使用POCO的我,review.Recommendations是一个ICollection<Recommendation>,所以我不能这样做review.Recommendations.Attach.我也没有使用自我跟踪实体,所以我需要手动处理图形/更改跟踪 - 这一直到现在都不是问题.

所以你可以想象这个场景:

评论:

  • 建议(ICollection<Recommendation>):
    • RecommendationOne(Recommendation)
    • 推荐二(Recommendation)

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

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

评论:

  • 建议(ICollection<Recommendation>):
    • RecommendationOne(Recommendation)
    • 推荐二(Recommendation)
    • 推荐三(Recommendation)

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

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

编辑

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

ReviewController

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

UserContentService

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

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);
}
Run Code Online (Sandbox Code Playgroud)

所以,Update(或AddDelete)库的方法需要调用对每一项建议,这取决于它的新的/修改/删除.

RPM*_*984 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
}
Run Code Online (Sandbox Code Playgroud)

而已.我甚至不需要我的_service.Update方法 - 因为我不再需要存根技巧 - 因为审查是在带有检索的图形中,并被ApplyCurrentValues替换为TryUpdateModel.

现在当然 - 这不是一个并发防范的解决方案.

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

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

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


Lad*_*nka 6

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

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

我回答类似的问题更深入地在这里.


Joh*_*ell 4

也许我需要更多背景信息,但有什么问题:

recommendations.Add(newRecomendation)
Run Code Online (Sandbox Code Playgroud)

回复评论:

好吧,那有什么问题吗

SomeServiceOrRepository.AddNewRecommendation( newRecommendation )
Run Code Online (Sandbox Code Playgroud)

或者

SomeServiceOrRepository.AddNewRecommendation( int parentId, newRecommendation )
Run Code Online (Sandbox Code Playgroud)

最后一句话?你是说这两个问题吗?

这应该不难。

总结我的答案,我认为您正在“以困难的方式”做事,并且确实应该专注于发布与您试图完成的 CRUD 操作相对应的表单值。

如果一个新实体可以与您编辑的实体同时出现,您实际上应该为它们添加不同的前缀,以便模型绑定器可以识别它。即使您有多个新项目,您也可以使用相同的 [0] 语法,只需在“名称”字段前添加 New 或其他内容即可。

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

如果表单是不可变的,您还可以尝试使用 ObjectSet 的通用附加函数:

theContect.ObjectSet<Review>().Attach( review )
Run Code Online (Sandbox Code Playgroud)

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