Question

I'm using the Fluent Validation framework in my ASP.net MVC 3 project. So far all of my validations have been very simple (make sure string is not empty, only a certain length, etc.) but now I need to verify that something exists in the database or not.

  1. Should Fluent Validation be used in this case?
  2. If the database validation should be done using Fluent Validation, then how do I handle dependencies? The validator classes are created automatically, and I would need to somehow pass it one of my repository instances in order to query my database.

An example of what I'm trying to validate might:

I have a dropdown list on my page with a list of selected items. I want to validate that the item they selected actually exists in the database before trying to save a new record.

Edit
Here is a code example of a regular validation in Fluent Validation framework:

[Validator(typeof(CreateProductViewModelValidator))]
public class CreateProductViewModel
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class CreateProductViewModelValidator : AbstractValidator<CreateProductViewModel>
{
    public CreateProductViewModelValidator()
    {
        RuleFor(m => m.Name).NotEmpty();
    }
}

Controller:

public ActionResult Create(CreateProductViewModel model)
{
    if(!ModelState.IsValid)
    {
        return View(model);
    }

    var product = new Product { Name = model.Name, Price = model.Price };
    repository.AddProduct(product);

    return RedirectToAction("Index");
}

As you can see, I never create the Validator myself. This works because of the following line in Global.asax:

FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure();

The problem is that now I have a validator that needs to interact with my database using a repository, but since I'm not creating the validators I don't know how I would get that dependency passed in, other than hardcoding the concrete type.

Was it helpful?

Solution

This link may help you implement what you are looking for without having to manually instantiate and manually validate you models. This link is directly from the FluentValidation discussion forum.

OTHER TIPS

Can't you just create your own validation method where in you would kick-off the database validation?

    RuleFor(m => m.name)
           .Must(BeInDatabase)

    private static bool BeInDatabase(string name)
    {
        // Do database validation and return false if not valid
        return false;
    }

I'm using FluentValidation for DataBase validations. just pass the Validation class the session in the Ctor. and do the validation inside the action something like:

var validationResult = new ProdcutValidator(session).Validate(product);

Update: Based on your example I add my example...

public class CreateProductViewModel
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public class CreateProductViewModelValidator : abstractValidator<CreateProductViewModel>
{
    private readonly ISession _session;
    public CreateProductViewModelValidator(ISession session)
    {
        _session = session;
        RuleFor(m => m.Name).NotEmpty();
        RuleFor(m => m.Code).Must(m, Code => _session<Product>.Get(Code) == null);

    }
}
Controller:

public ActionResult Create(CreateProductViewModel model)
{
    var validator = new CreateProductViewModelValidator();
    var validationResult =validator.Validate(model);

    if(!validationResult.IsValid)
    {
        // You will have to add the errors by hand to the ModelState's errors so the
        // user will be able to know why the post didn't succeeded(It's better writing 
        // a global function(in your "base controller" That Derived From Controller)
        // that migrate the validation result to the 
        // ModelState so you could use the ModelState Only.
        return View(model);
    }

    var product = new Product { Name = model.Name, Price = model.Price };
    repository.AddProduct(product);

    return RedirectToAction("Index");
}

Second update:
If you insist using parameterless constructor you will have to use some Inversion Of control container, a static class that is something like the Factory of your objects. use it like this:

public class CreateProductViewModelValidator : abstractValidator<CreateProductViewModel>
{
    private readonly ISession _session;
    public CreateProductViewModelValidator()
    {
        _session = IoC.Container.Reslove<ISession>();
        RuleFor(m => m.Name).NotEmpty();
        RuleFor(m => m.Code).Must(m, Code => _session<Product>.Get(Code) == null);

    }
}

You can find many IoC containers, most famous are Windsor and Ninject, You will need to register- instruct the container once to resolve all the ISession to return your's session object.

The other way this could work for you is using Constructor injection. While this method isn't as clear cut as using an IoC library, it may help if you have a static way of accessing or fetching your session.

public class CreateProductViewModelValidator
{
    private ISession _session;

    public CreateProductViewModelValidator()
        :this(SessionFactory.GetCurrentSession()) //Or some other way of fetching the repository.
    {

    }

    internal CreateProductViewModelValidator(ISession session)
    {
        this._session = session;
        RuleFor(m => m.Name);//More validation here using ISession...
    }
}

I have been spending quite a bit of time thinking about this exact same issue. I am using ninject to inject my repository into my web UI layer so that my web UI only accesses the database through an interface.

I am wanting to be able to validate things that access the database such as checking for duplicate names and hence my validation needs to access the injected repository. I think that the best way to do this is to just setup Fluent Validation via the manual method rather than the MVC integrated way. For Example:

Create your validation Class (can pass in repository Interface):

public class CategoryDataBaseValidation : AbstractValidator<CategoryViewModel>
{

    private IRepository repository;

    public CategoryDataBaseValidation (IRepository repoParam) 
    {

        repository = repoParam;

        RuleFor(Category => Category.Name).Must(NotHaveDuplicateName).WithMessage("Name already exists");
    }

    private bool NotHaveDuplicateName(string name) 
    {

       List<Category> c = repository.Categories.ToList(); //Just showing that you can access DB here and do what you like.
       return false;


    }
}

}

Then in your controller you can just create an instance of above class and pass in the repository (that ninject would have injected in the controller constructor)

 [HttpPost]
    public ActionResult Create(CategoryViewModel _CategoryViewModel )
    {

        CategoryDataBaseValidation validator = new CategoryDataBaseValidation (repository);

        ValidationResult results = validator.Validate(_CategoryViewModel );

       if (results.IsValid == false)
        {

            foreach (var failure in results.Errors)
            {

              //output error

            }

        }

        return View(category);
    }

Both the above files can live in the Web UI project and you can then also just use the standard MVC DataAnnotations for client side validation.

Just thought that I would put this up for comment / help someone.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top