我与评论和建议之间存在1 .. *关系。
我的模型的相关部分(也是EF4映射的POCO):
public class Review
{
public ICollection<Recommendations> Recommendations { get; set; }
}
在修改视图上,我将推荐表示为一组复选框。
当我尝试添加新建议书作为编辑评论的一部分时(例如检查另一个框),什么也没发生 - 我知道为什么......
我使用“存根技术”来更新我的实体 - 例如我创建一个具有相同键的实体,将其附加到图表,然后执行ApplyCurrentValues
。但这仅适用于标量属性,而不适用于导航属性。
我发现this StackOverflow question看起来不错,但我正在尝试研究如何使用POCO的/ Repository(以及ASP.NET MVC - 分离的上下文)。
由于我正在使用POCO,review.Recommendations
是ICollection<Recommendation>
,所以我无法review.Recommendations.Attach
。我也没有使用自我跟踪实体,所以我需要手动处理图形/更改跟踪 - 直到现在这还不是问题。
所以你可以看到这个场景:
修改:
ICollection<Recommendation>
):
Recommendation
)Recommendation
)如果我在编辑视图中,则已经选中了两个复选框。第三个(代表RecommendationThree)是未选中。
但如果我检查该框,则上述模型变为:
修改:
ICollection<Recommendation>
):
Recommendation
)Recommendation
)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
(或Add
或Delete
)存储库方法,具体取决于它是新的/修改/删除的。
答案 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
方法 - 因为我不再需要存根技巧 - 因为审核在图表中包含检索,ApplyCurrentValues
被TryUpdateModel
替换
现在当然 - 这不是并发性解决方案。
如果我加载了评论编辑视图,在我点击“提交”之前,其他人更改了评论,我的更改可能会丢失。
幸运的是,我有一个“最后赢”的并发模式,所以对我来说这不是问题。
我喜欢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 )
远离这种情况的方法很多。也许您可以发布您的控制器并查看代码?