2016-07-22 8 views
0

Ich bin im Autofac rum und habe Probleme bei der Bindung an einen bestimmten Konstruktor.AutoFac NamedParameter wird nicht richtig aufgelöst

Ich habe den folgenden Code:

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; 

Dann in der Fabrik:

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) 
     //); 
    } 
} 

Dies ein Produkt löst aber nicht mit dem Konstruktor i erwartet.

Ziel Konstruktor

public Product(bool returnEmpty) 

Beheben Konstruktor

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

Es gibt insgesamt 23 Konstrukteure und das eine Lösung ist nicht das größte (so dass ich nicht denke, daß seine gierig)

dh

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

Noch ist es das erste oder letzte in oder der Definition.

Ich bin ratlos kann jemand sehen, was ich falsch mache.

+0

Warum brauchen Sie DI für Objekte, die DTO zu sein scheint? Sehen Sie sich das an: http: // stackoverflow.com/questions/6297322/dependency-injection-use-with-data-transfer-objects-dtos oder http://programmers.stackexchange.com/questions/83091/use-dependency-injection-for-data-objects –

+0

@CyrilDurand Leider ist dies eine Megaklasse des Schicksals (eine unter vielen) und kein DTO-Objekt. Ich werde versucht, so ioc-Struktur auch das Projekt zu implementieren, ohne alles auf einmal damit die Notwendigkeit zu tun, die Konstruktoren nicht zu vereinfachen, da die Objekte sowohl injizieren und nicht injizieren und immer noch funktionieren müssen. Ich bin so nah an einer Lösung, dass ich posten werde, was ich mit diesem arvo vorhabe. – Spaceman

Antwort

0

So wieder auf der doco nachzulesen. Ich musste den Konstruktor am Anfang binden. Aber das behebt mein Problem nicht, also habe ich einen anderen Container gemacht, immer wenn eine Instanz angefordert wird, und erstelle sie basierend auf den Parametern. Es ist ein wenig inkorrekt, aber dies ist eine echte Welt Lösung für jeden, der auf Autofac für eine sehr große bestehende Lösung wechselt.

Ich hoffe, das hilft jemandem.

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); 
    } 
} 

Einrichtung und Nutzung sieht wie folgt aus etwas:

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

Leider Autofac diesen Mechanismus nicht bietet.

Sie könnten IConstructorSelector implementiert haben, die einen Konstruktor auswählen, wenn mehr als ein Konstruktor verfügbar ist, und ihn mithilfe der Methode UsingSelector auf die Registrierung festlegen, aber leider gibt es keine Möglichkeit, auf die verfügbaren Parameter der aktuellen Auflösungsoperation zuzugreifen.

Eine andere Lösung wäre die Implementierung von IInstanceActivator, die für die Erstellung der Instanz basierend auf dem Typ und den Parametern zuständig ist. Um eine benutzerdefinierte IInstanceActivator zu verwenden, müssen Sie auch implementieren IRegistrationBuilder, die ziemlich schwierig ist. Um eine gute Leistung zu garantieren, würde ich auch die Verwendung von ConstructorParameterBinding empfehlen, die eine optimierte Factory mit dynamischem kompiliertem Ausdruck erstellen wird.

Wenn Sie Ihren Konstruktor nicht ändern können, ist die einzige Lösung, die ich sehen kann, eine eigene Fabrik zu implementieren. Da Ihr Objekt keine Abhängigkeiten hat, können Sie sie ohne Verwendung von Autofac erstellen.

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 
    } 
}