1
votes

Attempting to inject data into a FluentValidation validator:

public class MyFormValidator : AbstractValidator<MyForm>
{
    private readonly IQueryable<Models.User> _users;

    public MyFormValidator(IQueryable<Models.User> users)
    {
        _users = users;
        ...
    }
}

My validator factory:

public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
    private readonly IContainer container;

    public DependencyResolverValidatorFactory(IContainer container)
    {
        this.container = container;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        return container.ResolveOptionalKeyed<IValidator>(validatorType);
    }
}

My Autofac configurator:

public class AutofacConfigurator
{
    public static void Configure()
    {
        var builder = new ContainerBuilder();
        ...

        builder.RegisterType<MyFormValidator>()
            .Keyed<IValidator>(typeof(IValidator<MyForm>))
            .As<IValidator>()
             // 2nd parameter returns IQueryable<User>
            .WithParameter("users", new SqlRepository<User>(dataContext)) 
            .InstancePerRequest();

        builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());

        var container = builder.Build();

        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

        // Register the validator factory with FluentValidation, and register 
        // FluentValidation as the model validator provider for the MVC framework. 
        // see http://www.jerriepelser.com/blog/using-fluent-validation-with-asp-net-mvc-part-3-adding-dependency-injection
        var fluentValidationModelValidatorProvider = 
            new FluentValidationModelValidatorProvider(
                new DependencyResolverValidatorFactory(container));
        DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
        fluentValidationModelValidatorProvider.AddImplicitRequiredValidator = false;
        ModelValidatorProviders.Providers.Add(fluentValidationModelValidatorProvider);

    }
}

Getting the following exception:

No scope with a Tag matching 'AutofacWebRequest' is visible from the scope in which the instance was requested. This generally indicates that a component registered as per-HTTP request is being requested by a SingleInstance() component (or a similar scenario.) Under the web integration always request dependencies from the DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime, never from the container itself.

I have other validators, most of which will not need data injected into them.

This is largely new ground for me (in both Autofac and FluentValidation) and am still trying to understand what I am doing here. I suspect I'm simply registering my type incorrectly. How do I fix this and properly register my type?

(My apologies if this is too similar to other questions that were already asked.)

1
Can you show place, where you call resolving of validator instance?Evgeny Levin

1 Answers

5
votes

I have zero experience with FluentValidation, but I doubt it's the cause of your issues anyway, so I'll plow forward regardless.

The exception you're getting means that Autofac can't resolve your service as 'instance per request'. There's a lot of documentation as to what this means on the Autofac documentation page. To summarize, it means that Autofac will attempt to resolve the service from a lifetime scope that is automatically created for each request sent to the webserver. When you register something as .InstancePerRequestScope() but then attempt to resolve that service outside of that scope, you'll get the DependencyResolutionException you see.

So we've established that your MyFormValidator isn't being resolved from a 'Request' scope. Why?

The custom DependencyResolverValidatorFactory you've written takes the actual IContainer that was built by Autofac, and resolves from that. This is a special type of ILifetimeScope, the 'root scope'. There's no request lifetime scope directly associated with this, so you get your exception. You need to to resolve from an ILifetimeScope that is began from the 'request' scope, or a sub-scope that is contained within the request scope.

The Autofac/MVC integration already automatically hosts a request scope (within the AutofacDependencyResolver, see the source), but your custom DependencyResolverValidatorFactory doesn't resolve from it. If you want to do that, I suppose you could modify your DependencyResolverValidatorFactory to accept the AutofacDependencyResolver instance instead, and use that to resolve.

It would look something like this:

public class DependencyResolverValidatorFactory : ValidatorFactoryBase
{
    private readonly AutofacDependencyResolver resolver;

    public DependencyResolverValidatorFactory(AutofacDependencyResolver resolver)
    {
        this.resolver = resolver;
    }

    public override IValidator CreateInstance(Type validatorType)
    {
        return resolver.RequestLiftimeScope.ResolveOptionalKeyed<IValidator>(validatorType);
    }
}

Note the RequestLifetimeScope stuck in there. Then you create this in your .Configure() method using

var resolver = new AutofacDependencyResolver(container);
DependencyResolver.SetResolver(resolver);

var fluentValidationModelValidatorProvider = 
    new FluentValidationModelValidatorProvider(
        new DependencyResolverValidatorFactory(resolver));

That should get rid of the exception, assuming that this factory does indeed have a request to work from when creating instances of IValidators. If not, You might need to register using the default behavior (.InstancePerDependency(), where it creates a new instance every time it's requested) or a singleton (.SingleInstance()), depending on how/if validators can or should be shared.

Good luck.