到目前为止,我们的大多数验证是使用视图模型上的验证属性执行的。
我们需要执行的另一项验证检查是验证数据库中是否已存在字符串。
最初我只是在控制器操作中处理此检查,然后在需要时向ModelState添加错误。但是,我宁愿使用内置的验证基础架构。
我尝试过的一种方法是在我的viewmodel上实现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>
或者IRepository<Bar>
取决于模型。
远程验证很棒,但我仍然需要验证服务器端。
那么人们会推荐或者用自己来实现类似的东西。
请注意,我对此列确实有唯一的数据库约束,但不希望回退到异常处理以执行此验证。
我看了asp.net articl e @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);
}
我在控制器操作中检查ValidateSlug(...)
之前就致电ModelState.IsValid
。
这是我目前需要的验证级别的一个很好的解决方案,以及大多数可以使用数据注释处理的事实。如果我的验证/业务规则变得更复杂,我可能会切换到FluentValidation(这也适用于Depenency Injection),因为它更适合外化验证逻辑。