Fortunately, I recently had the chance to work with FluentValidation, which offers a fantastic alternative to Data Annotations for validating models.

Although the official documentation claims that FluentValidation implementation is straightforward, it does not cover how to set it up with Dependency Injection.

The fundamentals of Ninject Dependency Injection are essential for this blog post. If you are unfamiliar with it, please refer to the documentation to learn more about it.

How to Configure FluentValidation via Dependency Injection?

To accomplish this, I am assuming we have already implemented FluentValidation to our project and are utilizing some sort of Dependency Injection framework. It's Ninject in my case. However, any code snippets here are easily transferable to other Dependency Injection frameworks of your choice.

As an example, we will create a StudentController and Student model as part of the configuration.

Create a Student Controller and Student Model

// Controller
[RoutePrefix("api/student")]
public class StudentController : ApiController
{
    [HttpPost]
    [ValidateModel]
    [Route("savestudent")]
    public HttpResponseMessage Create(Student student)
    {
       SaveStudent(student);
       return student;
    }
}

// Model
[Validator(typeof(StudentValidator))]
public class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Department { get; set; }
}

Next, we can create a validator class for the Student model.

public class StudentValidator : AbstractValidator<Student>
{
    public StudentValidator()
    {
        RuleFor(model => model.FirstName)
            .NotEmpty();
            
        RuleFor(model => model.LastName)
            .NotEmpty();

        RuleFor(model => model.Department)
            .NotEmpty();
    }
}

Now we will configure the FluentValidation to WebAPI in WebApiconfig.cs

FluentValidationModelValidatorProvider.Configure(config);

Suppose you want to resolve any Ninject DI in your model validator like in the example below. In that case, you cannot do it by following the standard process for constructing FluentValidation outlined above.

public class StudentValidator: AbstractValidator <Student> {
    private readonly ISomeService SomeService;
    public StudentValidator(ISomeService someService) {
      SomeService = someService;

      RuleFor(model => model.FirstName)
        .NotEmpty();

      When(x => SomeService.IsLastNameRequired(), () => {
          RuleFor(model => model.LastName)
            .NotEmpty();
        });

        RuleFor(model => model.Department)
        .NotEmpty();
      }
    }

The code above will produce the following exception.

{
    "Error": "No parameterless constructor defined for this object."
}

To make the code above function, we must first create a custom ValidationFactory, which will be supplied as a second argument when configuring FluentValidation in WebApiConfig.cs

Create a Custom Validator Factory?

We will create a custom ValidatorFactory class inherited from ValidatorFactoryBase class.

 public class ValidatorFactory: ValidatorFactoryBase {
    private readonly IDependencyResolver ServiceProvider;

    public ValidatorFactory(IDependencyResolver provider) {
      ServiceProvider = provider;
    }

    public override IValidator CreateInstance(Type validatorType) {
      var modelClass = validatorType.GetGenericArguments()[0];
      var validatorAttribute = modelClass.GetCustomAttributes(typeof (ValidatorAttribute), false).FirstOrDefault() as ValidatorAttribute;
      if (validatorAttribute != null) {
        var service = ServiceProvider.GetService(validatorAttribute.ValidatorType);
        return service as IValidator;
      }
      return null;
    }
  }

We must add [Validator(typeof(ValidatorName))] to our models,  If you are following the above approach. In our case, Student the model will be decorated as [Validator(typeof(StudentValidtor))].

Configure FluentValidation to use our custom validator factory

The last step is to configure FluentValidation to use our custom validator factory.

FluentValidationModelValidatorProvider.Configure(config, options => {
   options.ValidatorFactory = new ValidatorFactory(config.DependencyResolver);
 });

Here, we are instructing FluentValidation to search for validators using our custom validation factory, which will only use DI to create an instance of a certain validator.

I hope this blog post helps you set up and use DI with your FluentValidators.