【发布时间】: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