2017-07-18 91 views
2

我使用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()'這裏使用的錯誤。 –

回答

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

太棒了。非常感謝。我會嘗試你的建議重構。我只想讓示例代碼先工作 –