2012-12-21 14 views
8

"Autofacは、コンテナから取得できる最も多くのパラメータを持つコンストラクタを自動的に選択します。私はそれが別の方法で行い、代わりにデフォルトのコンストラクタを選択したいと思います。 http://code.google.com/p/autofac/wiki/Autowiringパラメータなしコンストラクタの自動選択

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 
} 

私はクラスを配線すると、私はこれをしなかった:

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

最初はconstructorBindingsリストに結合が常にparemeterlessのコンストラクタを有するものです。それが最初に定義されているかどうか、あるいはautofacがコンストラクタをスキャンする方法はわかりませんが、これはパラメータのないコンストラクタのための正しい方法でしょうか?

答えて

6

Autofacが内部コンストラクタを発見するType.GetConstructorsメソッドを使用しています

感謝。方法documentationから

そのような宣言の順序として、 特定の順序のコンストラクタを返さないGetConstructors方法。コード は、コンストラクタが返される順番に依存してはならない。なぜなら、その順序は であるからである。

あなたのケースではFirst()と一緒に働くのはちょうど運が良かったです。適切な実装では、明示的に0の引数でコンストラクタを検索する必要があります。

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

あなたは、この非常に単純なクラスで理論をテストすることができますあなたの実装に

public class MyClass 
{ 
    public readonly int i; 

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

    public MyClass() 
    { 
     i = 1; 
    } 
} 

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

22を出力します。例えば、int引数を持つコンストラクタは、

となります。私の実装では

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

それは出力1例えばデフォルトコンストラクタが呼び出されます。

+0

私は運が良かったと私は知っていたFirst():)詳細な説明をいただきありがとうございます。 –

+0

Autofacは 'InvalidOperationException'の代わりに、パラメータのないctorが見つからなければより適切な' DependencyResolutionException'クラスを持つようになりました。 – aholmes

1

デフォルトコンストラクタを明示的に明示的に登録する方が簡単ではないでしょうか? Autofacの最近のバージョンでは

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

はい、この単純なケースでは機能します。しかし、一度に複数の型を登録すると(例えば 'RegisterAssemblyTypes'で)、カスタムの' IConstructorSelector'を実装する方がよりクリーンな解決策になります。 – nemesv

+5

最新バージョンのAutofacには、適切な型のコンストラクタを選択するUsingConstructor(params Type []パラメータ)オーバーロードがあります。パラメータなしで呼び出すと、デフォルトのコンストラクタが選択されます。 – Alexander

1

、これは非常に簡単です:パラメータなしで "UsingConstructor" を呼び出す

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

は、 "パラメータなしのコンストラクタを使用する" を意味します。 https://autofac.org/apidoc/html/EB67DEC4.htmおよび関連ページを参照してください。

関連する問題