AutoFac NamedParameter没有正确解析

问题描述:

Im在autofac和im有一些问题绑定到特定的构造函数。AutoFac NamedParameter没有正确解析

我有以下代码:

var builder = new ContainerBuilder(); 

builder 
    .RegisterType<GenericIocFactory>() 
    .As<IGenericIocFactory>(); 

builder 
    .RegisterType<Product>() 
    .As<IProduct>() 
    .PropertiesAutowired(); 

IContainer Container = builder.Build(); 

IGenericIocFactory Fac = Container.Resolve<IGenericIocFactory>(); 

_product = Fac.Get<IProduct>(new Dictionary<string,object>() { {"returnEmpty" , false} }) as Product; 

然后在工厂:

public interface IGenericIocFactory 
{ 
    T Get<T>(Dictionary<string,object> options) where T: class; 
} 

public class GenericIocFactory : IGenericIocFactory 
{ 
    private readonly IComponentContext _icoContext; 
    private object _options; 

    public GenericIocFactory(IComponentContext icoContext,bool isInjected = true) 
    { 
     _icoContext= icoContext; 
    } 

    public T Get<T>(Dictionary<string,object> options) where T: class 
    { 
     var _parameters = new List<Parameter>(); 
     foreach (var parameter in options) 
     { 
      _parameters.Add(new NamedParameter(parameter.Key, parameter.Value)); 
     } 
     return _icoContext.Resolve<T>(_parameters); 
     //operate on new object 

     // tried this as well 
     //return _icoContext.Resolve<T>(
      //new NamedParameter("returnEmpty" , false) 
      //new TypedParameter(typeof(bool),false) 
     //); 
    } 
} 

这解决了一个产品,但不与构造我的期望。

目标构造

public Product(bool returnEmpty) 

解决构造

public Product(IList<string> productCodes, string fields = "", string orderBy = "ProductCode") 

有总共23个构造函数和一个解决还不是最大的(所以我不认为它是贪婪)

public Product(string strFields, string strFrom, string strFilter, string strOrderBy, string whseCode, 
     bool addExistsInWharehouse, string additionalAfterorderBy, bool forceUniqueRecords = false) 

也不是定义中的第一个也不是最后一个。

Im难怪任何人都可以看到我做错了什么。

+0

为什么你需要DI对象似乎是DTO?看看这个:http:// *。com/questions/6297322/dependency-injection-use-with-data-transfer-objects-dtos or http://programmers.stackexchange.com/questions/83091/use-dependency-injection-for-data-objects –

+0

@CyrilDurand不幸的是,这是一个巨大的厄运类(其中很多)不是DTO对象。我试图实现这样的ioc结构项目,而不是一次做所有的事情,因此需要不简化构造函数,因为对象需要既注入又不注入,仍然功能相同。我非常接近解决方案,我会发布我提出的这个arvo。 – Spaceman

所以在doco上再读一遍。我需要在开始时绑定构造函数。但是这不会解决我的问题,所以我创建了另一个容器,一次请求实例并基于参数构建它。它有点不正确,但对于任何正在向大型现有解决方案过渡到autofac的人来说,这是一个真正的世界性解决方案。

希望这可以帮助别人。

public interface IGenericIocFactory 
{ 
    T Get<T>(params object[] constructorParams) where T: class; 
} 

public interface ICustomAutoFacContainer 
{ 
    IContainer BindAndReturnCustom<T>(IComponentContext context, Type[] paramsList); 
} 

public class CustomAutoFacContainer : ICustomAutoFacContainer 
{ 
    public IContainer BindAndReturnCustom<T>(IComponentContext context, Type[] paramsList) 
    { 
     if (context.IsRegistered<T>()) 
     { 
      // Get the current DI binding type target 
      var targetType = context 
       .ComponentRegistry 
       .Registrations 
       .First(r => ((TypedService) r.Services.First()).ServiceType == typeof(T)) 
       .Target 
       .Activator 
       .LimitType; 

      // todo: exception handling and what not .targetType 

      var builder = new ContainerBuilder(); 

      builder 
       .RegisterType(targetType) 
       .As<T>() 
       .UsingConstructor(paramsList) 
       .PropertiesAutowired(); 

      return builder.Build(); 
     } 
     return null; 
    } 
} 

public class GenericIocFactory : IGenericIocFactory 
{ 
    private ICustomAutoFacContainer _iCustomContainer; 
    private readonly IComponentContext _icoContext; 
    public GenericIocFactory(ICustomAutoFacContainer iCustomContainer, IComponentContext icoContext) 
    { 
     _iCustomContainer = iCustomContainer; 
     _icoContext = icoContext; 
    } 

    public T Get<T>(params object[] constructorParams) where T: class 
    { 
     //TODO handle reflection generation? ?? ?not needed?? ?? 

     var parameters = constructorParams 
      .Select((t, index) => new PositionalParameter(index, t)) 
      .Cast<Parameter>() 
      .ToList(); 

     var parameterTypes = constructorParams 
      .Select((t, index) => t.GetType()) 
      .ToArray(); 

     return _iCustomContainer 
      .BindAndReturnCustom<T>(_icoContext,parameterTypes) 
      .Resolve<T>(parameters); 
    } 
} 

安装和使用情况看起来是这样的:

var builder = new ContainerBuilder(); 

// Usually you're only interested in exposing the type 
// via its interface: 
builder 
    .RegisterType<GenericIocFactory>() 
    .As<IGenericIocFactory>(); 

builder 
    .RegisterType<CustomAutoFacContainer>() 
    .As<ICustomAutoFacContainer>(); 

builder 
    .RegisterType<Product>() 
    .As<IProduct>() 
    .PropertiesAutowired(); 

var container = builder.Build(); 

var factory = container.Resolve<IGenericIocFactory>(); 

_product = factory.Get<IProduct>(false) as Product; 
_product = factory.Get<IProduct>("","") as Product; 

不幸的是Autofac不提供这种机制。

您可能已经实现IConstructorSelector当多个构造函数可用时选择一个构造函数,并使用UsingSelector方法将其设置为注册,但很遗憾,无法访问当前解析操作的可用参数。

另一种解决方案是实现IInstanceActivator,它负责根据类型和参数创建实例。要使用自定义IInstanceActivator,您还需要实施IRegistrationBuilder,这非常困难。为了保证良好的性能,我还建议使用ConstructorParameterBinding这将使用动态编译表达式创建优化的工厂。

如果你不能改变你的构造函数,我能看到的唯一解决方案就是实现你自己的工厂。由于您的对象没有任何依赖关系,因此您可以在不使用Autofac的情况下创建它们。

public class GenericIocFactory : IGenericIocFactory 
{ 
    public GenericIocFactory(ILifetimeScope scope) 
    { 
     this._scope = scope; 
    } 

    private readonly ILifetimeScope _scope; 

    public T Get<T>(params object[] args) where T: class 
    {   
     ConstructorInfo ci = this.GetConstructorInfo(args); 
     if (ci == null) 
     { 
      throw ... 
     } 

     var binder = new ConstructorParameterBinding(ci, args, this._scope); 

     T value = binder.Instanciate() as T; 

     if (value == null) 
     { 
      throw ... 
     } 
     if(value is IDisposable) 
     { 
      this._scope.Disposer.AddInstanceForDisposal(value); 
     } 
     return value; 
    } 


    protected virtual ConstructorInfo GetConstructorInfo<T>(params object[] args) 
    { 
     // TODO 
    } 
}