views:

43

answers:

3

I'm getting a 'not registered error' during execution in the ValidatorFactory.CreateInstance call. It appears the type being sent into the method is correct.

My registration code:

...
builder.RegisterAssemblyTypes(assembly).Where(t => t.Name.EndsWith("Validator")).As<IValidator>();
builder.Register(d => _containerProvider).As<IContainerProvider>();
builder.Register(d => new ValidatorFactory(_containerProvider.ApplicationContainer.Resolve<IContainerProvider>())).As<IValidatorFactory>();

_containerProvider = new ContainerProvider(builder.Build());

My ValidatorFactory code:

public class ValidatorFactory : ValidatorFactoryBase {
    private readonly IContainerProvider _containerProvider;

    public ValidatorFactory(IContainerProvider containerProvider) {
        _containerProvider = containerProvider;
    }

    public override IValidator CreateInstance(Type validatorType) {
        return _containerProvider.ApplicationContainer.Resolve<IValidator>(validatorType);
    }
}

My view model and validator code:

[Validator(typeof(EmployeeViewModelValidator))]
public class EmployeeViewModel {
    public EmployeeViewModel() {}
    public EmployeeViewModel(string firstName) {
        FirstName = firstName;
    }

    public string FirstName { get; private set; }
}

public class EmployeeViewModelValidator : AbstractValidator<EmployeeViewModel> {
    public EmployeeViewModelValidator() {
        RuleFor(x => x.FirstName)
            .NotEmpty();
    }
}

My best guess is that I'm registering the validators wrong.

+1  A: 

To resolve from multiple classes that implement IValidator, the autofac documentation shows a couple of techniques. I don't think you can use assembly scanning with these methods; you'll have register the validators individually.

By Name

builder.RegisterType<EmployeeViewModelValidator>().Named<IValidator>("employee");

var r = container.Resolve<IValidator>("employee");

By Key

The documentation shows an enumeration being used as a key, but you could use the type as the key, which would let you call Resolve using the validatorType being passed to you in the CreateInstance method.

builder.RegisterType<EmployeeViewModelValidator>().Keyed<IValidator>(typeof(EmployeeViewModelValidator));    

var r = container.Resolve<IValidator>(validatorType);

Note: The documentation is for version 2.3 of Autofac. I'm using 2.2.4, which is why the method names above vary from the documentation.

Hope this helps.

adrift
The reason for the name change is reflected in this scenario - `Resolve<IValidator>(validatorType)` is misleading, because the `validatorType` is a key rather than the service type. Hence in 2.3 this is `ResolveKeyed<IValidator>(validatorType)` :) ... good answer!
Nicholas Blumhardt
@Nicholas Blumhardt, thanks and thank you for Autofac :)
adrift
Thanks for the response! See my below still errors out.
Mike
+1  A: 

The other answer by @adrift is a nice clean approach. Just to add some notes, this also works with the IIndex<K,V> relationship type, which Autofac provides automatically - e.g.:

public class ValidatorFactory : ValidatorFactoryBase {
    readonly IIndex<Type, IValidator> _validators;

    public ValidatorFactory(IIndex<Type, IValidator> validators) {
        _validators = validators;
    }

    public override IValidator CreateInstance(Type validatorType) {
        return _validators[validatorType];
    }
}

Important note - don't use IContainerProvider and ApplicationContainer in a component - instead use IComponentContext or `IIndex as above. This will help prevent lifetime issues (your example associates validators with the application rather than the request lifetime.)

HTH!

Nick

Nicholas Blumhardt
Thanks for the response! See my below still errors out.
Mike
A: 

Current Registration Code:

builder.RegisterType<EmployeeViewModelValidator>().Keyed<IValidator>(typeof(EmployeeViewModel));
builder.RegisterType<ValidatorFactory>().As<IValidatorFactory>();

_containerProvider = new ContainerProvider(builder.Build());

Current ValidatorFactory:

public class ValidatorFactory : ValidatorFactoryBase {
    readonly IIndex<Type, IValidator> _validators; 

    public ValidatorFactory(IIndex<Type, IValidator> validators) {
        _validators = validators;
    } 

    public override IValidator CreateInstance(Type validatorType) {
        return _validators[validatorType];
    }
}

Same error inside CreateInstance:

The requested service 'FluentValidation.IValidator`1[.EmployeeViewModel] (FluentValidation.IValidator)' has not been registered.

Mike
in your registration, I believe it should be `typeof(EmployeeViewModelValidator)` since that would be the type passed in to `CreateInstance`
adrift