2016-07-22 34 views
0

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:// stackoverflow。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

回答

0

所以在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; 
1

不幸的是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 
    } 
}