根据您对应用程序的描述,它似乎是一个相当标准的应用程序,您按照预期的方式使用 DataAnnotations 应该没有任何问题。我将在这里对应用程序的结构做出一些假设(以填补缺失的细节),并将解释我认为您可以如何使用 DataAnnotations 进行业务验证。
我假设业务层由反映您的应用程序域的对象模型组成。域模型中的每个对象都公开了实现业务规则的属性和潜在方法。这是您要在其中编写验证代码的应用程序层,并且确实是域验证规则的合适位置。
我假设您正在为数据访问层使用实体框架或类似的 ORM。
您的 MVC 控制器不直接访问域模型。相反,ViewModel(您称它们为数据传输对象)用于为各个视图适当地构造数据。因此,ViewModel 包含从域模型中抽象出来的数据,域验证规则适用于这些数据。
现在,这一切都很好,并且符合预期。如果您希望覆盖特定视图的域验证规则,您可以将其他 DataAnnotations 添加到 ViewModel 中的属性,但这不是必需的。但是,虽然 MVC 将针对视图模型属性执行客户端验证,但它不会针对底层域对象。
因此,ModelState.IsValid
在这种情况下使用该方法将不起作用。要在域模型上执行验证,您可以DbEntityValidationException
在尝试保存对象后捕获:
[HttpPost]
public ActionResult Edit(int id, BlogView view)
{
try
{
Blog blog = Mapper.Map(view); //map view model to domain object using eg. Automapper
db.Entry(blog).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch(DbEntityValidationException ex)
{
var error = ex.EntityValidationErrors.First().ValidationErrors.First();
this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
return View();
}
}
如果不想依赖 EntityFramework 抛出的异常,可以使用以下DbContext.GetValidationErrors()
方法显式触发验证:
foreach (var validationResults in db.GetValidationErrors())
{
foreach (var error in validationResults.ValidationErrors)
{
Debug.WriteLine("Entity Property: {0}, Error {1}",
error.PropertyName,error.ErrorMessage);
}
}
或者,您的域对象可以实现该IValidatableObject
接口,并使用其 Validate() 方法:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (Title == BloggerName)
{
yield return new ValidationResult
("Blog Title cannot match Blogger Name", new[] { "Title", “BloggerName” });
}
}
有关更多详细信息,请参阅http://msdn.microsoft.com/en-gb/data/gg193959.aspx。