简体   繁体   中英

How to use FluentValidation with LightInject in asp.net web-api project

I'm trying to inject a service using the IoC container into a Validation class. See the example below:

[Validator(typeof(UserPayloadValidator))]
public class UserPayload
{
    public int UserId { get; set; }
}

public class UserPayloadValidator : AbstractValidator<UserPayload>
{
    private IUserService _userService;

    public UserPayloadValidator(IUserService userService)
    {
        _userService = userService;

        RuleFor(x => x.UserId).Must(BeUnique).WithMessage("This user already exists");
    }

    private bool BeUnique(int userId)
    {
        var user = _userService.GetUser(userId);

        return user == null;
    }
}

At this point I was hoping everything would auto-magically work and the userService would be injected into the validation class. Instead, I get an exception complaining about a parameter-less constructor not being found.

After some reasearch I've attempted to create a ValidationFactory as in the example linked.

public class LightInjectValidationFactory : ValidatorFactoryBase
{
    private readonly ServiceContainer _serviceContainer;

    public LightInjectValidationFactory(ServiceContainer serviceContainer)
    {
        _serviceContainer = serviceContainer;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        return _serviceContainer.TryGetInstance(validatorType) as IValidator;
    }   
}

and in the LightInject configuration

//Set up fluent validation
FluentValidationModelValidatorProvider.Configure(httpConfiguration, provider =>
{
    provider.ValidatorFactory = new LightInjectValidationFactory(container);
});

This results in an exception: Unable to resolve type: FluentValidation.IValidator`1

I guess the IoC container doesn't know how to resolve the correct instance for the validator.

Any ideas are much appreciated.

Thanks to the comment above I realized I wasn't actually registering the validator in container. This can be done like this for all the validators:

FluentValidation.AssemblyScanner.FindValidatorsInAssemblyContaining<UserPayloadValidator>()
            .ForEach(result =>
            {
                container.Register(result.InterfaceType, result.ValidatorType);
            });

Please note that UserPayloadValidator needs to be just one of your validators. Based on this type, FindValidatorsInAssembly can infer all the other available validators.

Also, in the validation factory you should use TryGetInstance instead of GetInstance in case the factory tries to instantiate non existant validators (parameter in the controller for which validators do not exist)

I have found solution for all validation classes use injected service.

Replace below code

FluentValidation.AssemblyScanner.FindValidatorsInAssemblyContaining<UserPayloadValidator>()
            .ForEach(result =>
            {
                container.Register(result.InterfaceType, result.ValidatorType);
            });

With

FluentValidation.AssemblyScanner findValidatorsInAssembly = FluentValidation.AssemblyScanner.FindValidatorsInAssembly(typeof(UserPayloadValidator).Assembly);
            foreach (FluentValidation.AssemblyScanner.AssemblyScanResult item in findValidatorsInAssembly)
            {
                container.Register(item.InterfaceType, item.ValidatorType);
            }

Using this your all validator classes use injected service.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM