私は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();
}
}
私の質問:
Autofacは、非パブリックコンストラクターを使用しません。デフォルトでは、公開されているもののみが検索され、他のユーザーは表示されません。 .FindConstructorsWith(BindingFlags.NonPublic)
を使用しない限り、パブリックコンストラクターのみが表示されます。したがって、シナリオは期待どおりに機能するはずです。
常にWithParameter
メソッドを使用して、コンストラクターパラメーターを明示的に指定できます。
builder.RegisterType<DoesSomething>()
.As<IDoesSomething>()
.WithParameter("helper", new HelperClass("do", "something"));
builder.RegisterType<DoesSomethingElse>()
.As<IDoesSomethingElse>()
.WithParameter("helper", new HelperClass("do", "somethingelse"));
私が知る限り、HelperClass
のインターフェイスは基本的に単なる値の保持者であるため、必要ありません。
これが機能するには、内部コンストラクターをパブリックにする必要があると思います。
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();
}
}