web-dev-qa-db-ja.com

Autofacを使用してコンストラクターにパラメーターを渡す

私はautofacが初めてなので、完全に誤用している可能性があります。

この構造を持つクラスがあるとしましょう:

public class HelperClass : IHelperClass
{
     public HelperClass(string a, string b)
     {
         this.A = a;
         this.B = b;
     }
}

そして、そのクラスを使用する2つのクラスがありますが、コンストラクターには異なるデフォルトが必要です。 2番目のコンストラクターはテスト目的のJUSTです。「実際の」アプリには常にHelperClassが必要です。

public class DoesSomething: IDoesSomething
{
     public DoesSomething()
         : this(new HelperClass("do", "something"));
     {

     }

     internal DoesSomething(IHelperClass helper)
     {
          this.Helper = helper;
     }
}

public class DoesSomethingElse : IDoesSomethingElse
{
     public DoesSomethingElse()
         : this(new HelperClass("does", "somethingelse"));
     {

     }

     internal DoesSomethingElse(IHelperClass helper)
     {
          this.Helper = helper;
     }
}

AutoFacモジュールは次のとおりです。

public class SomethingModule: Module
{
    protected override void Load(ContainerBuilder builder)
    {
         builder.RegisterType<DoesSomething>().As<IDoesSomething>();
         builder.RegisterType<DoesSomethingElse>().As<IDoesSomethingElse();
    }
}

私の質問:

  1. DoesSomethingまたはDoesSomethignElseでresolveを呼び出すと、パブリックコンストラクタではなく内部コンストラクタが解決されますか? IHelperClassを未登録のままにする必要がありますか?
  2. はいの場合、DoesSomethingまたはDoesSomethingElseで使用されているかどうかに応じて、IHelperClassの各インスタンスに異なるパラメーターを渡すようにするにはどうすればよいですか?
37
Paul

Autofacは、非パブリックコンストラクターを使用しません。デフォルトでは、公開されているもののみが検索され、他のユーザーは表示されません。 .FindConstructorsWith(BindingFlags.NonPublic)を使用しない限り、パブリックコンストラクターのみが表示されます。したがって、シナリオは期待どおりに機能するはずです。

9
Pavel Gatilov

常にWithParameterメソッドを使用して、コンストラクターパラメーターを明示的に指定できます。

builder.RegisterType<DoesSomething>()
       .As<IDoesSomething>()
       .WithParameter("helper", new HelperClass("do", "something"));

builder.RegisterType<DoesSomethingElse>()
       .As<IDoesSomethingElse>()
       .WithParameter("helper", new HelperClass("do", "somethingelse"));

私が知る限り、HelperClassのインターフェイスは基本的に単なる値の保持者であるため、必要ありません。

これが機能するには、内部コンストラクターをパブリックにする必要があると思います。

55
Daniel Hilgarth

Autofacでパラメーターを渡すには2つの方法があります。

コンポーネントを登録する場合

コンポーネントを登録すると、そのコンポーネントに基づいたサービスの解決中に使用できる一連のパラメーターを提供できます。 Autofacは、いくつかの異なるパラメーターマッチング戦略を提供します。

  • NamedParameter-名前でターゲットパラメータに一致
  • TypedParameter-タイプごとにターゲットパラメータを一致させます(正確なタイプの一致が必要です)
  • ResolvedParameter-柔軟なパラメーターマッチング

    // Using a NAMED parameter:
    builder.RegisterType<ConfigReader>()
       .As<IConfigReader>()
       .WithParameter("configSectionName", "sectionName");// parameter name, parameter value. It's the same of this: new NamedParameter("configSectionName", "sectionName")
    
    // Using a TYPED parameter:
    builder.RegisterType<ConfigReader>()
       .As<IConfigReader>()
       .WithParameter(new TypedParameter(typeof(string), "sectionName"));
    
    // Using a RESOLVED parameter:
    builder.RegisterType<ConfigReader>()
       .As<IConfigReader>()
       .WithParameter(
         new ResolvedParameter(
           (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
           (pi, ctx) => "sectionName"));
    

    NamedParameterおよびTypedParameterは定数値のみを提供できます。

    ResolvedParameterは、コンテナから動的に取得された値を提供する方法として使用できます。名前でサービスを解決します。

IConfigurationなどの登録済みのサービスをパラメーターとして渡したい場合は、以下に示すようにパラメーターを解決できます。

    builder.RegisterType<Service>()
           .As<Iervice>()
           .WithParameter((pi, ctx) => pi.ParameterType == typeof(IConfiguration) && pi.Name == "configuration",
                          (pi, ctx) => ctx.Resolve<IConfiguration>());

コンポーネントを解決するとき

Autofacで実行時にパラメーターを渡す1つの方法は、Resolveメソッドを使用することです。次のようなクラスを作成できます。

public class ContainerManager
{
  public IContainer Container {get;set;}
  //...
  public T[] ResolveAllWithParameters<T>(IEnumerable<Parameter> parameters)
  {
    return Container.Resolve<IEnumerable<T>>(parameters).ToArray();
  }
}

ParameterはAutofacに属する抽象クラスです。NamedParameterクラスを使用して、必要なパラメーターを渡すことができます。以下に示すように、ContainerManagerクラスを使用できます。

    public T[] ResolveAllWithParameters<T>(IDictionary<string,object> parameters )
    {
        var _parameters=new List<Parameter>();
        foreach (var parameter in parameters)
        {
            _parameters.Add( new NamedParameter(parameter.Key, parameter.Value));
        }
        return ContainerManager.ResolveAllWithParameters<T>(_parameters);
    }

これにより、特定のコンポーネントを解決するときに、Dictionary<string, object>を使用して実行時にパラメーターを渡すことができます。

拡張メソッド を使用すると、さらに簡単になります。

public static class ContainerExtensions
{
    public static T[] ResolveAllWithParameters<T>(this IContainer Container, IDictionary<string, object> parameters)
    {
        var _parameters = new List<Parameter>();
        foreach (var parameter in parameters)
        {
            _parameters.Add(new NamedParameter(parameter.Key, parameter.Value));
        }
        return Container.Resolve<IEnumerable<T>>(_parameters).ToArray();
    }
}
44
octavioccl