Asp.net核心AutoFac注册使用通用工厂
问题描述:
我使用Asp.net核心与AutoFac这里之后接受的答案:Asp.net核心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");
}
}
}
答
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;
}
}
+0
太棒了。非常感谢。我会尝试你的建议重构。我只想让示例代码先工作 –
我远离我的电脑,所以无法编译您的代码 - 具体问题是什么?你看到一行错误'return(IValidator)container.Resolve(valType)'?或者这个调用不返回任何东西? –
@ChimaOsuji问题是容器只有在使用'.Build()'后才可用,所以我没有看到我能做到这一点。我需要在调用'.Build()'之后注册这个服务,然后再次调用'.Build()'或者'.Resolve()'这里使用的错误。 –