【发布时间】:2011-05-31 12:03:39
【问题描述】:
到目前为止,我们的大部分验证都是使用视图模型上的验证属性执行的。
我们需要执行的另一个验证检查是验证我们的数据库中不存在字符串。
最初我只是在控制器操作中处理此检查,然后在需要时将错误添加到 ModelState 中。但是,我更愿意使用内置的验证基础设施。
我尝试的一种方法是在我的视图模型上实现 IValidateableObject。当我调用 DependencyResolver 时,这感觉有点不对:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var viewModel = validationContext.ObjectInstance as EditPostViewModel;
if (viewModel != null)
{
var slug = (Slug)viewModel.Slug;
var repo = DependencyResolver.Current.GetService<IRepository<Post>>();
var existing = repo.Get(p => p.Slug == slug && p.Id != viewModel.Id);
if (existing != null)
yield return new ValidationResult("Duplicate slug.", new[] { "Slug" });
}
}
我想到的另一种方法是使用自定义 ValidationAttribute。对我来说,这只有在我可以在多个视图模型上重用它并重用它时才有意义,我需要能够构造一个通用存储库接口(根据上面的代码),因为我可能需要 IRepository<Foo> 或 @987654324 @ 取决于型号。
远程验证很棒,但我仍然需要验证服务器端。
那么人们会推荐或使用自己来实现类似的目标。
请注意,我在此列上确实有唯一的数据库约束,但不想回退到异常处理来执行此验证。
解决方案
我按照asp.net article @Darin 的建议进行了查看。这种方法确实有效,但是这篇文章有一点缺陷,虽然它设法将验证服务与对 ModelState 的任何直接引用分离,但你最终会得到一个循环依赖,其中控制器依赖于验证服务,而验证服务依赖于在 ModelState 上(通过包装器);在创建控制器之前不存在。好的!
相反,我将 IValidationDictionary 公开为我的验证服务上的公共属性,并在我的控制器构造函数中设置它:
slugValidator.ValidationDictionary = new ModelStateWrapper(this.ModelState);
其余部分是特定于应用程序的,但基本上我为我想要验证的每种类型的“slugable”实体创建了一个验证器。然后这些由我的容器注入。
public interface ISlugValidator<TEntity> where TEntity : ISlugable {
IValidationDictionary ValidationDictionary { get; set; }
bool ValidateSlug(Guid? entityId, Guid featureId, Slug slug);
}
我在控制器操作中检查ModelState.IsValid 之前调用ValidateSlug(...)。
这是一个很好的解决方案,可以满足我目前需要的验证级别,而且大部分验证都可以使用数据注释来处理。如果我的验证/业务规则变得更复杂,我可能会切换到 FluentValidation(这也适用于依赖注入),因为它更适合外部化验证逻辑。
【问题讨论】:
标签: asp.net-mvc validation asp.net-mvc-3