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

时间:2010-10-07 01:37:55

标签: asp.net-mvc design-patterns validation entity-framework-4 poco

以下是该方案:

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

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

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

  1. 控制器在服务层
  2. 上调用添加(发布)方法
  3. 服务层调用
  4. 上的添加(T)
  5. 存储库在自定义数据上下文
  6. 上调用 AddObject(T)
  7. 控制器在工作单元上调用提交()
  8. 现在,我正在尝试找出可以进行验证的地方。

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

    1. 简单,独立的POCO验证,例如“帖子必须有标题”。这似乎很适合POCO的Data Annotations
    2. 复杂的业务验证,例如“无法向已锁定的帖子添加评论”。这不能通过数据注释完成。
    3. 现在,我一直在阅读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();
      

      这会抛出一个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
         }
      }
      

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

      所以为了防止这是一个“到处都是”的问题,这是我的问题:

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

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

        感谢。

        修改

        以下答案很有帮助,但我仍然(理想情况下)寻找更多想法。还有其他人吗?

2 个答案:

答案 0 :(得分:1)

  1. 就像你说的那样,DataAnnotations不适合所有情况。根据我的经验,缺点主要是复杂的验证(多个属性和多个属性不同的对象)。
  2. 如果我是你,我会尽可能地将业务/域验证从数据层(EF)中删除。如果有数据层验证方案,那么很好(例如验证复杂的父/子关系 - 这纯粹是DB的东西)。
  3. 是的,复杂的业务验证应该在服务层或模型对象中(通过部分类或某些继承方法附加:接口/派生类)。 ActiveRecord人员,Repository Pattern人员和DDD人员之间存在争议,但是对于适合您的方式,这很简单,并且可以实现快速部署和低成本的应用程序维护。这是一个简单的example of how you might attach more complex validation to domain objects,但仍然与DataAnnotations接口兼容,因此是'MVC友好'。
  4. 好问题。 - 我还没有找到解决方案我还是100%满意。我玩过私人制定者的想法并不是很好。快速阅读这个summarized Evans DDD book。这是一个很好的快速阅读,它可能提供一些关于模型对象和值对象之间的目的和区别的见解。这就是我认为对象设计将减轻您对财产“篡改”(如您所说)的问题但不修复属性可见性的问题。即,另一种解决方案可能在其他地方。希望这会有所帮助。

答案 1 :(得分:1)

嘿,可能有点晚了,但无论如何都要去......

这完全取决于您的架构,即您的应用程序中是否存在逻辑分离:UI,服务层,存储库层。如果您正在关注Save事件,那将如何完成?根据我的观察,你只会调用存储库Layer for Persistance吗?但是,您正在连接到保存事件,将控制权交还给服务层/业务层,然后通过权限强制保存?

我个人认为服务层/业务层应该在完成时处理它然后说,嘿先生的repo层 - >保存这个对象。

关于验证,数据注释应该与UI一起使用,如[Required]等简单的valiation,这将有助于客户端验证,但复杂的业务逻辑或复杂的验证应该挂钩到服务层/业务层,这样它可以在所有实体/对象/ POCOS等中重复使用。

关于防止某些私有字段未被篡改...只允许您的服务层/业务层实际设置将被持久化的对象(是的,我的意思是:) ...)手工编码,我觉得无论如何,这对我来说是最安全的选择,我会这么做:

var updatedpost = _repo.GetPost(post.postid);
updatedpost.comment = post.comment;
updatedpost.timestamp = datetime.now;

有点浪费,但这样你的buseinss层控制,但这只是我的经验我可能是错的,我已经阅读了很多关于模型绑定,validaiton和其他的东西,但似乎有一些事情,事情永远不会工作预期的例如[必填]属性(见Brad WIlson的)帖子。