使用异常验证Asp.net MVC中的业务逻辑

Alt*_*ept 7 validation asp.net-mvc

我有一个关于我在asp.net mvc中用于执行业务规则验证的方法的问题.

目前我有一个看起来像这样的异常类

public class ValidationException : Exception 
{
    private ModelStateDictionary State { get; set; }
    public ValidationException(ModelStateDictionary state)
    {
        State = state;
    }
    public void MergeModelStates(ModelStateDictionary state)
    {
        state.Merge(this.State);
    }
}
Run Code Online (Sandbox Code Playgroud)

和一个看起来像这样的验证器

public void Validate(IEntity entity)
{
    ModelStateDictionary state = new ModelStateDictionary();
    if (entity.Contact != null && _service.GetBy(entity.Contact.Id) == null)
        state.AddModelError("Contact", "Invalid Contact.");
    if (entity.Title.Length > 8)
        state.AddModelError("title", "Title is too long...");
    ... etc
    if (!state.IsValid)
        throw new ValidationException(state);
}
Run Code Online (Sandbox Code Playgroud)

和控制器做这样的事情

public ActionResult Add()
{
    var entity = new InputModel;
    try
    {
        TryUpdateMode(inputModel);
        ..... Send input to a Repository (Repository calls Validate(entity);
    }
    catch (ValidationException validationException)
    {
       validationException.MergeModelStates(this.ModelState);
       TryUpdateModel(inputModel);
       return View("Add",inputModel);
    }
    return View("List");
}
Run Code Online (Sandbox Code Playgroud)

使用异常来做这样的事情是错误的吗?有更好的方法的例子吗?我真的不想将验证添加到模型实体本身.我看到它完成的另一种方法是将Controllers ModelState注入到Repository层,但这对我来说似乎很草率.

谢谢你的帮助

Rya*_*ner 14

例外通常应该用于例外情况,而不是处理在程序正常执行期间经常发生的事情.这有很多很好的理由 - 这里有一些我经常遇到的问题:

  1. 性能问题 - 例外通常是相当昂贵的操作 - 如果经常抛出您的性能可能会受到影响.
  2. 处理未捕获的验证异常 - 如果您碰巧使用您的代码而不处理异常,则会将验证错误显示为"黄色屏幕"或崩溃处理程序 - 可能不是最佳用户体验.
  3. 例外的结构不是允许面向用户的信息.看看异常类 - 没有多少设置方式可以提供良好的面向用户的信息,这是将信息转发回用户所需要的.每当我试图以这种方式使用异常时,我最终会得到一大堆具有属性的子类,这些子类并没有真正意义上的异常.

我通常喜欢的一种方法是提供一个公共Validate方法,它返回一个错误列表(但从不抛出异常),然后是一个调用Validate()的Save方法,如果有任何错误则抛出异常.您可以将行为从"如果模型无效时抛出"切换为"如果代码在模型处于无效状态时尝试保存则抛出".

要解决以下关于Validate vs. Save中抛出的性能的评论 - 抛出Save()将与抛出Validate()具有完全相同的性能损失.然而,关键的区别在于,这应该永远不会发生 - 您正在防止使用您的类不正确的开发人员,而不是使用异常作为验证方法.正确编写,调用save方法的代码应如下所示:

ValidationResult result = obj.Validate();
if (result.IsValid) {
   obj.Save();
} else {
   // display errors to the user
}
Run Code Online (Sandbox Code Playgroud)

如果开发人员在保存之前忘记检查验证状态,则仅抛出异常.这样做的好处是既可以在不使用异常的情况下进行验证,也可以通过永不允许保存无效实体来保护数据库.理想情况下,您根本不会在控制器中捕获异常并让一般错误处理例程处理它,因为问题不再是用户输入而是开发人员的错误.