【问题标题】:Preferred methods for business entity validation in the enterprise企业中业务实体验证的首选方法
【发布时间】:2012-03-30 06:37:06
【问题描述】:

让我作为这个问题的序言,并声明我们不能选择使用实体框架。

在我们的财务组织中,我们拥有将跨解决方案使用的业务实体。有些有用户界面,有些则没有。验证和业务规则必须包含在实体中。

我针对为我生成的 DAL 和 DTO 进行编码,这些 DAL 使用 procs 在数据库上操作 CRUD(可能是 SQL,也可能是 Oracle)。

所以当我创建 MVC、WCF、控制台应用程序等时,问题一直在困扰着是否可以实施更好的验证方法。

以下是实体对象中的几个典型属性:

[DefaultValue("")]
public string Branch {
    get { return _branch; }
    set {
        if (value != null && value == _branch) return;
        const string propertyName = "Branch";
        ValidationInstance.Clear(propertyName);
        ValidationInstance.ValidateRequired(propertyName, value);
        ValidationInstance.ValidateNumeric(propertyName, value);
        ValidationInstance.ValidateLength(propertyName, value, 2);
        _branch = value;
        if (EntityState != EntityStateType.New)
            EntityState = EntityStateType.Changed;
    }
}

    [DefaultValue(0)]
public decimal HighDefermentMargin {
    get { return _highDefermentMargin; }
    set {
        if (value == _highDefermentMargin) return;
        const string propertyName = "HighDefermentMargin";
        ValidationInstance.Clear(propertyName);
        ValidationInstance.ValidateRange(propertyName, value);
        _highDefermentMargin = value;
        if (EntityState != EntityStateType.New)
            EntityState = EntityStateType.Changed;
    }
}

如您所见,混合了数据注释和对验证类的显式调用,以执行越来越详细的验证。

在 MVC 应用程序中,我们煞费苦心地在 ViewModel 上重复验证,以便获得客户端和服务器端验证。这是上面相同属性的 ViewModel 版本:

[Required]
[Range(0.0, 99.99)]
[Display(Name = "High Deferment Margin")]
public decimal HighDefermentMargin { get; set; }

这里的主要区别是实体中的验证将错误加载到 Validation 类的错误集合中,可以在实体进行自我保存时查询该集合。 If(!IsValid) 然后抛出一个包含错误数组的自定义异常。控制器循环遍历它们并将它们添加到 ModelState。

我开始研究一些实际上有几百个字段的类。即使它们被 OO 分解,字段的数量仍然非常多。这些是贷款证明等,其中包含大量数据用于单个记录。不得不写出对这么多属性的验证让我想呕吐。我不能只编写一个实用程序来生成实体和验证,因为驱动验证的是业务规则,而不是数据库。这意味着一个字段可以在数据库中为空,但不允许根据业务规则保留为空,或者该字段可以为空,但仅当单独的字段具有值时,等等。

那么,仅使用视图模型和实体中的数据注释是否可以以相同的方式获得相同的结果?我可以为非标准验证编写自定义验证器,然后为更复杂的东西编写业务规则。验证错误是否会从实体提升到更高级别,以便 UI 可以以与 ModelState 相同的友好方式通知用户?在这种情况下,其他人在做什么?

【问题讨论】:

  • 我有一个建议。在通过业务规则引擎验证时遇到无效输入或规则未满足的任何情况,只需冒泡异常,以便可以在 Controller 中捕获它,然后将其添加到模型状态的错误集合中。例如:抛出一个自定义异常并且不在规则引擎中捕获该异常,只需重新抛出,直到它到达控制器。然后使用 Html.ValidationSummary() 在 UI 中显示。
  • 这可行——但我们的想法是避免在 set 或 get 中评估属性,并提供更多的 AoP 设计模式。我已经在属性中制定了收集错误的策略。抛出异常所做的只是提供了一种向调用者显示错误的不同方式。那只是将代码交换为不同的代码。我想从属性中删除代码。
  • 内置类型默认为required,不需要添加该属性。

标签: asp.net-mvc-3 validation design-patterns c#-4.0


【解决方案1】:

通常,属性上的验证属性可以获得不同的验证结果(验证是否通过),具体取决于它是在 UI 层、业务层还是 DAL 中进行评估。例如,如果在 ViewModel 上应用 Required 属性,它可能会失败,但在业务层中它可以通过,因为用户没有提供的值是从其他来源提供的。 然而,像电子邮件格式这样的验证规则……总是得到相同的结果。但是,存在可能需要在业务层重复验证的安全性问题……仅仅是因为 Web 服务器更容易受到攻击并且更容易受到攻击。但是,重复两次相同的验证并不意味着编写两次代码。您可以在一个公共 dll 中收集您希望在多个层中应用的所有属性,在该 dll 中定义您应用到同一概念实体的各种版本(ViewModel、BL、DAL)的MetaDataTypes。这样,您无需复制代码即可满足安全要求。

根据 saravanan 的建议,您可以使用异常将在其他层中发现的验证错误传达给 UI 层(您也可以配置 WCF 以将异常详细信息传达给客户端)

【讨论】:

  • 这是一个有趣的策略。我将不得不了解更多有关 MetaDataTypes 的信息,看看是否可以实现我正在寻找的东西。不确定这是否会成为问题,但我的 ViewModel 通常是多个实体的子集,并且不会 1:1 映射。你假设我想要一个强化的实体重新验证是对的,即使它通过了 UI。例如,如果 UI 被破坏并且 ViewModel 将实体设置为“活动”状态。实体中的业务规则和验证将评估和否定攻击。我需要看到一些好的替代策略的具体例子。
  • “ViewModel 通常是多个实体的子集,不映射”。您可以将 Just 1 MetaDataType 应用于每个对象,因此我建议这样做:不是将多个对象的属性混合到一个唯一的根类中,而是定义一个根 ViewModel。这种根 ViewModel 的每个属性都包含一个子视图模型,其属性是 JUST ONE BL 对象的属性的子集。这样每个子 ViewModel 可能有不同的 MetadataType。由多个属性组合而成的属性(例如价格总和直接放在根模型或您想要的 wgere 中
  • 这样,每个子 ViewModel 都有一个包含在 MetadataType 中的属性子集。现在......对于一种设计错误,如果元数据类型无法在它所应用的对象中找到所有它的属性,它会抛出一个异常(很难理解为什么???)。但是,我编写了一个自定义 DataAnnotationsMetaDataprovider 来克服这个问题。我把它放在我的开源项目“Mvc Controls Toolkit”中,但是如果你只需要 DataAnnotationMetaDataProvider 我可以给你一个独立的文件,你可以安装在你的项目中
  • 我明白你在说什么。我实际上已经这样做了,所以看起来我可以实现它。在持久化时如何处理验证?目前,我可以使用带有错误集合的验证类: public void Save(string username) { if (!IsDirty) return; if (!IsValid) throw new ELMValidationException("Errors", ErrorList);并将 ErrorList 发送到控制器并将它们添加到 ModelState。使用属性时如何在保存时进行评估?是否有要检查的错误包概念?
  • 如果您不介意发送,自定义提供程序听起来是一个不错的起点。 cdsmith72 at me dot com
猜你喜欢
  • 2011-07-13
  • 1970-01-01
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
  • 2012-01-03
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
相关资源
最近更新 更多