プリミティブ構成値と (サービス) 依存関係を同じコンストラクターに混在させることは避けるのが最善です。それらは DI 構成を複雑にし、多くの場合、判読不能で脆弱な構成ルートにつながります。
代わりに次の操作を行います。
オプション 1: その構成値を抽象化する
同じ構成値が複数のクラスに注入されると、多くの場合、抽象化が失われます。この良い例は、string connectionString
すべてのリポジトリへの値の注入です。この場合、アプリケーションにはIConnectionFactory
抽象化 (または類似のもの) が欠けている可能性があります。接続文字列を多くのクラスに注入する代わりに、接続文字列を実装に注入し(おそらく、その接続文字列のみをコンストラクター引数として取ります)、他のサービスがの代わりにIConnectionFactory
依存できるようにする必要があります。このように、これらのサービスは接続の作成を処理する必要がありません。接続ファクトリーはこれを行うことができます。IConnectionFactory
string
オプション 2: 構成値を構成タイプにラップする
プリミティブ型は、型の解決に関してあいまいさを引き起こします。注入されたのは、ファイル パスですか、それとも接続文字列ですかstring
? あなたint
が注入しているのは、再試行の回数ですか、それとも顧客が商品を購入できる最低年齢ですか?
あいまいさを避けるために、クラスが 1 つの値しか必要としない場合でも、クラスのすべての構成値を独自の構成タイプにラップすることをお勧めします。SqlUnitOfWorkSettings
をラップするthis を例にとりますTimeSpan
。
public sealed class SqlUnitOfWorkSettings
{
public readonly TimeSpan ConnectionTimeout;
public SqlUnitOfWorkSettings(TimeSpan connectionTimeout)
{
this.ConnectionTimeout = connectionTimeout;
}
}
TimeSpan
に依存する代わりに、 は代わりにSqlUnitOfWork
依存できるようになりましSqlUnitOfWorkSettings
た。
public sealed class SqlUnitOfWork : IUnitOfWork
{
private readonly SqlUnitOfWorkSettings settings;
public SqlUnitOfWork(SqlUnitOfWorkSettings settings)
{
this.settings = settings;
}
}
新しい構成クラスを使用すると、あいまいさがなくなるため、DI コンテナーへの登録が簡素化されます。
container.RegisterInstance(
new SqlUnitOfWorkSettings(connectionTimeout: TimeSpan.FromSeconds(30));
container.Register<IUnitOfWork, SqlUnitOfWork>(Lifestyle.Scoped);
オプション 3: サブタイプを作成する
構成クラスの使用がオプションではない場合 (たとえば、ソース コードを変更できないため)、コンポジション ルートに配置されたサブタイプを派生させ、適切なコンストラクターを定義し、代わりにそのサブタイプを登録できます。
public class MyService : SomeExternalService
{
public MyService(ISomeDependency dep) : base(dep, "My Config Value") { }
}
// Registration
container.Register<IService, MyService>();