Autofac with FluentValidation in MVC2
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.
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.
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<K,V>
as above. This will help prevent lifetime issues (your example associates validators with the application rather than the request lifetime.)
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.
精彩评论