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"); 
     } 
    } 
} 
+0

我远离我的电脑,所以无法编译您的代码 - 具体问题是什么?你看到一行错误'return(IValidator)container.Resolve(valType)'?或者这个调用不返回任何东西? –

+0

@ChimaOsuji问题是容器只有在使用'.Build()'后才可用,所以我没有看到我能做到这一点。我需要在调用'.Build()'之后注册这个服务,然后再次调用'.Build()'或者'.Resolve()'这里使用的错误。 –

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

太棒了。非常感谢。我会尝试你的建议重构。我只想让示例代码先工作 –