我正在使用Asp.net Core和AutoFac并遵循接受的答案:
Validation: How to inject A Model State wrapper with Ninject?
这使用ninject。我不明白如何在autoFac中执行相当于此ninject部分的操作,特别是kernel.Get
:
Func<Type, IValidator> validatorFactory = type =>
{
var valType = typeof(Validator<>).MakeGenericType(type);
return (IValidator)kernel.Get(valType);
};
kernel.Bind<IValidationProvider>()
.ToConstant(new ValidationProvider(validatorFactory));
Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var containerBuilder = new ContainerBuilder();
IValidator ValidatorFactory(Type type)
{
var valType = typeof(Validator<>).MakeGenericType(type);
//This line is the problem
// return (IValidator)container.Resolve(valType);
}
containerBuilder.Register(x => new ValidationProvider(ValidatorFactory)).As<IValidationProvider>().SingleInstance();
containerBuilder.RegisterType<UploadValidator>().As<Validator<AudioModel>>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return container.Resolve<IServiceProvider>();
}
问题是容器只有在使用.Build()
后才可用,所以我不知道如何做到这一点。我是否需要在致电.Build()
后注册此服务,然后再次致电.Build()
或.Resolve()
在此处使用错误的内容。
验证类:
internal sealed class ValidationProvider : IValidationProvider
{
private readonly Func<Type, IValidator> _validatorFactory;
public ValidationProvider(Func<Type, IValidator> validatorFactory)
{
_validatorFactory = validatorFactory;
}
public void Validate(object entity)
{
var results = _validatorFactory(entity.GetType()).Validate(entity).ToArray();
if (results.Length > 0)
throw new ValidationException(results);
}
public void ValidateAll(IEnumerable entities)
{
var results = (
from entity in entities.Cast<object>()
let validator = _validatorFactory(entity.GetType())
from result in validator.Validate(entity)
select result).ToArray();
if (results.Length > 0)
throw new ValidationException(results);
}
}
public abstract class Validator<T> : IValidator
{
IEnumerable<ValidationResult> IValidator.Validate(object entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
return Validate((T)entity);
}
protected abstract IEnumerable<ValidationResult> Validate(T entity);
}
public class UploadValidator : Validator<AudioModel>
{
protected override IEnumerable<ValidationResult> Validate(AudioModel model)
{
if (string.IsNullOrWhiteSpace(model.Name))
{
yield return new ValidationResult("Name", "Name is required");
}
}
}
答案 0 :(得分:1)
Autofac具有强大的功能,使我们能够register factories基于参数创建实例。在您的示例中,我们可以使用Autofac注册Func<Type, IValidator>
,并将其自动注入我们的ValidationProvider
。
var builder = new ContainerBuilder();
builder
//register our factory function
.Register<Func<Type, IValidator>>(
x =>
{
//get a reference to the scoped container
//e.g. if this is a web app, each HTTP request will
//spawn a child container used for the lifetime of that request
var context = x.Resolve<IComponentContext>();
return type =>
{
//create the validator from our scoped container
var valType = typeof(Validator<>).MakeGenericType(type);
return (IValidator) context.Resolve(valType);
}
}
)};
public class ValidationProvider
{
readonly Func<Type, IValidator> _factory;
//Autofac will see this class requires our previously registered
//function and inject this for us
public ValidationProvider(Func<Type, IValidator> factory)
{
_factory = factory;
}
}
作为替代方案,您是否可以使用泛型参数约束IValidator
?也许重构代码是不可行的,但如果是这样的话,可能更好的做法是为我们的服务提供他们所需的确切依赖,而不是可能隐藏其意图的工厂。
public interface IValidator<T>
{
void Validate(T instance);
}
public class SomeClassRequiringAudioModelValidator
{
readonly IValidator<AudioModel> _validator;
public SomeClassRequiringAudioModelValidator(IValidator<AudioModel> validator)
{
_validator = validator;
}
}