我对我在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);
}
}
和一个看起来像这样的验证器
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);
}
和一个像这样做的控制器
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");
}
使用异常来做这样的事情是不对的? 有更好的方法的例子吗?我真的不想将验证添加到模型实体本身。我看到它完成的唯一另一种方法是将Controllers ModelState注入到Repository层,但这对我来说似乎很草率。
感谢您的帮助
答案 0 :(得分:13)
异常通常应该用于特殊情况,而不是处理在程序正常执行期间经常发生的事情。这有很多很好的理由 - 这里有一些我经常遇到的问题:
我通常喜欢的一种方法是提供一个公共Validate方法,它返回一个错误列表(但从不抛出异常本身),然后是一个调用Validate()的Save方法,如果有的话会抛出异常错误。您可以将行为从“如果模型无效时抛出”切换为“如果代码在模型处于无效状态时尝试保存则抛出”。
要解决以下关于Validate vs. Save中投掷性能的评论 - 抛出Save()将与抛出Validate()具有完全相同的性能损失。然而,关键的区别在于,这应该永远不会发生 - 您正在防止使用您的类不正确的开发人员,而不是使用异常作为验证方法。正确编写,调用save方法的代码应该类似于:
ValidationResult result = obj.Validate();
if (result.IsValid) {
obj.Save();
} else {
// display errors to the user
}
如果开发人员在保存之前忘记检查验证状态,则仅抛出异常。这样做的好处是既可以在不使用异常的情况下进行验证,也可以通过永不允许保存无效实体来保护数据库。理想情况下,您根本不会在控制器中捕获异常并让一般错误处理例程处理它,因为问题不再是用户输入而是开发人员的错误。
答案 1 :(得分:2)
理想情况下,您将使用ASP.NET MVC 2中的数据注释功能: -
http://weblogs.asp.net/scottgu/archive/2009/07/31/asp-net-mvc-v2-preview-1-released.aspx
例如: -
public class Person
{
[Required(ErrorMessage="Please enter a name.")]
public String Name { get; set; }
}
如果你没有升级,还有一个解决方案:
如果您传递对Controller的ModelState字典的引用(如果您担心关注点分离,将其包含在接口内)到Validator,如果发现错误则调用AddModelError,然后在Controller中调用if( ModelState.IsValid)并采取适当的行动: -
var entity = new InputModel();
TryUpdateModel(entity);
MyValidator.Validate(entity, ModelState);
if(ModelState.IsValid) { ...
并且您的Validator看起来像: -
public void Validate(IEntity entity, ModelStateDictionary state)
{
if (entity.Contact != null && _service.ValidId(entity.Contact.Id) == null)
state.AddModelError("Contact", "Invalid Contact.");
// etc
}