2012-12-21 13 views
8

"Autofac automatycznie wybiera konstruktora z największą liczbą parametrów, które można uzyskać z pojemnika." Chcę, aby działał inaczej i zamiast tego wybierz domyślny konstruktor. http://code.google.com/p/autofac/wiki/AutowiringAutofac wybór parametrów bez parametrów

internal class ParameterlessConstructorSelector : IConstructorSelector 
{ 
    #region Implementation of IConstructorSelector 

    /// <summary> 
    /// Selects the best constructor from the available constructors. 
    /// </summary> 
    /// <param name="constructorBindings">Available constructors.</param> 
    /// <returns> 
    /// The best constructor. 
    /// </returns> 
    public ConstructorParameterBinding SelectConstructorBinding(ConstructorParameterBinding[] constructorBindings) 
    { 
     return constructorBindings.First(); 
    } 

    #endregion 
} 

Kiedy okablowania klasy, zrobiłem to:

builder.RegisterType<EmployeeFactory>() 
     .As<IEmployeeFactory>().UsingConstructor(new ParameterlessConstructorSelector()) 
     .SingleInstance(); 

Pierwszy wiąże w liście constructorBindings jest zawsze jeden z konstruktora paremeterless. Nie jestem pewien, czy zdefiniował pierwszy, czy sposób, w jaki autofac skanuje konstruktorów, ale czy jest to właściwe podejście do drutu dla konstruktora bez parametrów?

Dzięki

Odpowiedz

6

Autofac wewnętrznie wykorzystuje metodę Type.GetConstructors odkryć konstruktorów.

z metod documentation:

Sposób GetConstructors nie powraca konstruktory w określonej kolejności, na przykład, aby zgłoszenia. Twój kod nie może zależeć od numeru od kolejki, w jakiej zwracane są konstruktory, ponieważ ta kolejność jest zmienna .

To było po prostu szczęście, że zadziałało w twoim przypadku z First(). W prawidłowej realizacji trzeba jawnie szukać konstruktora z 0 argumentami:

public class DefaultConstructorSelector : IConstructorSelector 
{ 
    public ConstructorParameterBinding SelectConstructorBinding(
     ConstructorParameterBinding[] constructorBindings) 
    { 
     var defaultConstructor = constructorBindings 
      .SingleOrDefault(c => c.TargetConstructor.GetParameters().Length == 0); 
     if (defaultConstructor == null) 
      //handle the case when there is no default constructor 
      throw new InvalidOperationException();     
     return defaultConstructor; 
    } 
} 

można przetestować teorię z tego bardzo prostego klasy:

public class MyClass 
{ 
    public readonly int i; 

    public MyClass(int i) 
    { 
     this.i = i; 
    } 

    public MyClass() 
    { 
     i = 1; 
    } 
} 

Dzięki swojej realizacji:

var builder = new ContainerBuilder(); 
// register 22 for each integer constructor argument 
builder.Register<int>(v => 22); 

builder.RegisterType<MyClass>().AsSelf() 
    .UsingConstructor(new ParameterlessConstructorSelector()); 
var c = builder.Build(); 
var myClass = c.Resolve<MyClass>(); 
Console.WriteLine(myClass.i); 

Wypisuje 22 np. Konstruktor z argumentem int jest nazywany:

Z moim realizacji:

//... 
builder.RegisterType<MyClass>().AsSelf() 
    .UsingConstructor(new DefaultConstructorSelector()); 
//... 
var myClass = c.Resolve<MyClass>(); 
Console.WriteLine(myClass.i); 

To wyprowadza 1 np domyślny konstruktor nazywa.

+0

Wiedziałem, że miałem szczęście z First() :) Dziękuję za szczegółowe wyjaśnienie. –

+0

Zamiast "InvalidOperationException", Autofac ma teraz klasę 'DependencyResolutionException', która jest bardziej odpowiednia, jeśli nie znaleziono bez parametrów Ctor. – aholmes

1

Czy nie byłoby prostsze po prostu jawne zarejestrowanie domyślnego konstruktora?

builder.Register<EmployeeFactory>(c => new EmployeeFactory()) 
    .As<IEmployeeFactory>() 
    .SingleInstance(); 
+1

Tak, może to działać w tym prostym przypadku. Ale jeśli zarejestrujesz wiele typów naraz (np. Z 'RegisterAssemblyTypes'), implementacja niestandardowego' IConstructorSelector' jest czystszym rozwiązaniem. – nemesv

+5

Najnowsze wersje autofac mają parametr OverConstructor (params Type []), który wybiera konstruktor z odpowiednimi typami - jeśli wywoływany bez parametrów, wybierze domyślny konstruktor. – Alexander

1

Z ostatnich wersjach Autofac, to jest bardzo proste:

builder.RegisterType<EmployeeFactory>() 
     .As<IEmployeeFactory>().UsingConstructor() 
     .SingleInstance(); 

Wywoływanie "UsingConstructor" bez parametrów oznacza "użyć konstruktora bez parametrów". Zobacz https://autofac.org/apidoc/html/EB67DEC4.htm i pokrewne strony.

Powiązane problemy