使用ASP.NET MVC/Entity Framework进行POCO验证的建议

RPM*_*984 13 validation asp.net-mvc design-patterns poco entity-framework-4

这是场景:

  • ASP.NET MVC2 Web应用程序
  • 实体框架4(纯POCO,自定义数据上下文)
  • 存储库模式
  • 工作单位模式
  • 依赖注入
  • 服务层调解控制器 - >存储库

基本上,所有很酷的东西.:)

基本UI操作的事件流程("添加帖子"):

  1. 控制器在服务层上调用Add(Post)方法
  2. 服务层调用存储库上的Add(T)
  3. 存储库在自定义数据上下文上调用AddObject(T)
  4. 控制器在工作单元上调用Commit()

现在,我正在尝试找出可以进行验证的地方.

在这个阶段,我需要两种类型的验证:

  1. 简单,独立的POCO验证,例如"帖子必须有标题".这似乎很适合POCO的数据注释.
  2. 复杂的业务验证,例如"无法向已锁定的帖子添加评论".这不能通过数据注释完成.

现在,我一直在阅读Julie Lerman 撰写的 "编程实体框架,第二版"(这是一个优秀的BTW),并且一直在寻找挂钩SavingChanges事件以执行"最后一分钟"验证.这将是一个很好的方法,以确保每当我做"某事"(添加,修改,删除)时总是会发生验证,但它也有点晚了IMO(因为项目已经在状态管理器中) - 所以我能做什么如果验证失败,删除它们?

我当然可以让我的POCO实现一个接口(比如"IValidatable"),并在此事件期间调用此接口上的方法.

但对于业务验证来说,这似乎"为时已晚" - 这是共识吗?

我基本上都在寻找指导,我正在尝试为复杂的业务逻辑设计一个可重用的智能验证方案,考虑到我的上述架构.

另一个曲线球 - 如你所知,POCO与EF意味着POCO拥有数据库中的所有属性 - 所以我可能有一个"PostID"属性,带有get/set访问器(因为EF需要获取/设置这些属性) ).

但问题是,"PostID"是一个标识列,那么我如何保护该字段不被明确设置?例如,如果我(由于某种原因)执行以下操作:

var post = service.FindSingle(10);
post.PostId = 10;
unitOfWork.Commit();
Run Code Online (Sandbox Code Playgroud)

这将抛出一个SqlException.我该如何防止这种情况?我无法"隐藏"该属性(使其成为私有属性,甚至是内部属性),因为POCO是与存储库分开组装的.

关于验证的说明 - 我计划创建自定义异常(从Exception派生).因此,当验证失败时,我需要抛出这些异常.

这样,我可以在我的控制器上编码这样的代码:

[HttpPost]
public ActionResult AddPost(Post post)
{
   try
   {
      IUnitOfWork uow = new UnitOfWork();
      postService.Add(post);
      uow.Commit();
   }
   catch(InvalidPostOperation ipo)
   {
      // add error to viewmodel
   }
}
Run Code Online (Sandbox Code Playgroud)

每次我添加时,我是否必须在服务层上手动进行验证?那我该如何处理Save?(因为这是在工作单元,而不是服务层).

因此,为了防止这是一个"到处都是"的问题,这是我的问题:

  1. 简单的POCO验证 - 应该使用数据注释吗?优点/缺点/陷阱?
  2. 在什么情况下(如果有的话)我们应该挂钩到EF数据上下文的SavingChanges事件以提供验证?
  3. 我应该在哪里进行复杂的业务验证?在服务明确,或POCO的方法(我可以从服务调用).我如何创建智能/可重用的方案?
  4. 我们如何"隐藏"POCO的自动生成属性不被篡改?

任何想法都将非常感激.

如果这篇文章"太长",请道歉,但这是一个重要问题,可以通过多种方式解决,所以我想提供所有信息,以便获得最佳答案.

谢谢.

编辑

以下答案很有帮助,但我仍然(理想情况下)寻找更多想法.还有谁?

Mat*_*caj 1

  1. 正如您所说,DataAnnotations并不适合所有情况。根据我的经验,缺点主要是复杂的验证(多个属性和多个属性不同的对象)。
  2. 如果我是你,我会尽可能将业务/域验证排除在数据层 (EF) 之外。如果存在数据层验证场景,那么很好(例如,验证复杂的父/子关系 - 这纯粹是数据库的东西)。
  3. 是的,复杂的业务验证应该在服务层或模型对象中(通过部分类或某种继承方法附加:接口/派生类)。ActiveRecord 人员、存储库模式人员和 DDD 人员之间对此存在争议,但选择适合您、简单且能够实现快速部署和低成本应用程序维护的方法。这是一个简单的示例,说明如何将更复杂的验证附加到域对象,但仍然与接口兼容DataAnnotations,因此是“MVC 友好的”。
  4. 好问题。- 我还没有找到一个让我 100% 满意的解决方案。我曾经考虑过私人二传手的想法,但效果并不好。快速阅读这本Evans DDD 总结书籍。这是一本很好的快速读物,它可能会提供一些关于模型对象和值对象之间的目的和区别的见解。这就是我认为对象设计将减轻您在属性“篡改”(如您所说)方面遇到的问题,但不修复属性可见性的问题。即,另一个解决方案可能位于其他地方。希望这可以帮助。