开发者

Recommended approach for model validation that needs to touch the Db

Up to now, most of our validation is performed using validation attributes on our view models.

One additional validation check we need to perform is to validate that a string doesn't already exist in our database.

Originally I was just handling this check within the controller action and then adding an error into ModelState if required. However, I would rather make use of the built-in validation infrastructure.

One method I tried was implementing IValidateableObject on my viewmodel. This feels a bit wrong as I'm calling 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[] {开发者_JAVA百科 "Slug" });
    }
}

Another approach I thought about was using a custom ValidationAttribute. To me this only makes sense if I can re-use it on multiple viewmodels and to re-use it I need to be able to construct a generic repository interface (as per the above code) as I may need IRepository<Foo> or IRepository<Bar> depending on the Model.

Remote Validation is great but I still need to the validation server side.

So what would people recommend or have used themselves to achieve something similar.

Note that I do have unique database constraints on this column but don't want to fall back to exception handling to perform this validation.

Solution

I had a look as the asp.net article @Darin suggested. This approach definitely works, but the article is a bit flawed in that whilst it manages to decouple the validation service away from any direct references to ModelState, you instead end up with a circular dependency where controller depends on the validation service and the validation service depends on ModelState (via a wrapper); which doesn't exist until the controller is created. Nice!

Instead I exposed the IValidationDictionary as a public property on my validation service and set this in my controllers constructor:

slugValidator.ValidationDictionary = new ModelStateWrapper(this.ModelState);

The rest is kind of application specific but essentially I created a validator for each type of "slugable" entity I wanted to validate. These were then injected by my container.

public interface ISlugValidator<TEntity> where TEntity : ISlugable {
    IValidationDictionary ValidationDictionary { get; set; }
    bool ValidateSlug(Guid? entityId, Guid featureId, Slug slug);
}

I call ValidateSlug(...) just before I check ModelState.IsValid in my controller actions.

This is a good solution for the level of validation I need currently and the fact that most of it can be handled using Data annotations. If my validation/business rules get more complex I will probably switch to FluentValidation (this also works well with Depenency Injection) as it's better suited for externalizing validation logic.


I would recommend doing this type of validation at the service layer and not bother with data annotations.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜