Arguments in the Validator constructor

Jun 18, 2013 at 7:51 AM
This is cross-posted from Stack Overflow: http://stackoverflow.com/questions/17081667/fluentvalidation-arguments-in-the-validator-constructor

I would like to know how to pass a parameter to the Validator constructor.

Here is what I would like to do with my validator. Notice the string MsgParam in the constructor:
public class RegisterModelValidator : AbstractValidator<RegisterModel>
{
    public RegisterModelValidator(string MsgParam) // <= Here
    {
        RuleFor(x => x.UserName)
            .NotNull()
            .WithMessage(MsgParam);
        RuleFor(x => x.Password)
            .NotNull()
            .Length(6, 100);
        RuleFor(x => x.ConfirmPassword)
            .Equal(x => x.Password);
    }
}
And my model, where I do not know if I can pass anything using data annotation:
// Find a way to pass a string to the validator
[FluentValidation.Attributes.Validator(typeof(RegisterModelValidator))]
public class RegisterModel
{
    public string UserName { get; set; }

    [DataType(DataType.Password)]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    public string ConfirmPassword { get; set; }
}
Is it possible to do such a thing?
Coordinator
Jun 18, 2013 at 7:57 AM
Hi

If you want to take parameters in the validator's constructor then you'll need to instantiate the validators manually rather than relying on the automatic creation.

Jeremy
Jun 18, 2013 at 9:03 AM
Hi Jeremy,

Thanks for your quick answer! I modified my model and removed the line [FluentValidation.Attributes.Validator(typeof(RegisterModelValidator))]

and added these lines in the controller:
public ActionResult Register(RegisterModel model)
{
    RegisterModelValidator rmv = new RegisterModelValidator("Message passed as param");
    var result = rmv.Validate(model);

    if (result.IsValid)
    {
        ...
    }
    else
    {
        foreach (var Error in result.Errors)
        {
            ModelState.AddModelError("Error", Error.ToString());
        }
    }
    return View(model);
}
It works well, but I have two new problems:
  1. The CSS class input-validation-error is not added to the TextBoxFor helper.
  2. The client-side validation does not work anymore.
Do you have an idea how to solve these?
Coordinator
Jun 18, 2013 at 9:07 AM
Hi

1) The css classes won't be added because the field names no longer match the keys in ModelState. You should use result.CopyToModelState(ModelState) rather than using a foreach loop to copy items to the modelstate dictionary (this is an extension method, so ensure you have the correct namespace imported)

2) This is correct - if you instantiate the validators manually then you won't have client-side validation. No way around this I'm afraid.

Jeremy
Jun 18, 2013 at 9:31 AM
Thanks again. Could you tell me more about how to use the CopyToModelState method? I tried finding it in the source code to no avail.
Coordinator
Jun 18, 2013 at 9:33 AM
My apologies, the method is AddToModelState not CopyToModelState. This extension method resides within the FluentValidation.Mvc namespace
Jun 18, 2013 at 9:57 AM
Nice! It works perfectly now.

Just in case, here is the final code in the controller:
using FluentValidation.Mvc;
...
public ActionResult Register(RegisterModel model)
{
    RegisterModelValidator rmv = new RegisterModelValidator("Message passed as param");
    var result = rmv.Validate(model);

    if (result.IsValid)
    {
        ...
    }
    else
    {
        result.AddToModelState(ModelState, null);
    }
    return View(model);
}
I will try to find a solution for the client-side validation now.