質問
ここでは非常に一般的な状況があります。そして、私がやっていることが業界標準から見て正しいのかどうか、何年もわかりませんでした。データベースに接続するアプリケーションを考えてみましょう。ただし、接続文字列は、ファイルや設定に保存されるのではなく、コマンド ライン パラメータとして渡されます。起動時、またはアプリケーションの起動時にデータベースが参照されます。
その接続文字列をアプリのスコープ内のどこかに保存する必要があります。私が見た中で最も一般的な方法は、接続文字列を保存するための get/set メソッドを備えたモジュールまたはグローバル クラスです。もう 1 つの方法は、シングルトンを使用することです。どちらのオプションを選択しても、DAL は必要に応じて GetConnectionString メソッドを通じて接続文字列にアクセスできます。
これを行うより良い方法はありますか?
アップデート: 私は構成ファイルを持っていません。たとえ持っていたとしても、アプリケーションインスタンスの存続期間中に接続文字列を一度読み取る必要があります。「任意のクラスに注入」の部分について詳しく説明していただけますか
解決
一般に、グローバル クラスであってもシングルトンであっても、グローバル状態は可能な限り避けるべきです。
理想的な解決策は、アプリケーションに構成から接続文字列をロードさせ、 注入する それを必要なクラスに追加します。アプリケーションのサイズに応じて、 IoC コンテナのような 団結 または ウィンザー城 役立つこともありますが、ソリューションの必須部分ではありません。
それがオプションではなく、(既存のコードベースなどの理由で) グローバル状態の維持に行き詰まっている場合、あなたが提案した 2 つのアプローチに大きな違いがあるかどうかはわかりません。
アップデート: 明確にするために、今は IoC コンテナに関することはすべて忘れてください。「インジェクト」とは、(クラスのコンストラクターまたはプロパティなどを介して) 「パラメーターとして渡す」という派手な方法にすぎません。
データ アクセス クラスが (ある種のグローバルまたはシングルトンから) 接続文字列を要求するのではなく、コンストラクターまたはプロパティを介して接続文字列を渡します。
アップデート #2: このアプローチが何を意味するかについては、まだ誤解があると思います。
基本的には、データ アクセス クラスが次のようになっているかどうかによって決まります。
public class DataAccessClass
{
public DataAccessClass()
{
_connString = SomeStaticThing.GetConnectionString();
}
}
または
public class DataAccessClass
{
public DataAccessClass(string connString)
{
_connString = connString;
}
}
これら 記事 (実際、そのブログの記事の多くは) 後者が前者よりも優れている理由をいくつか詳しく説明しています (特に、前者は単体テストがほぼ不可能であるため)。
はい、で どこかの場所 最初に接続文字列の取得を担当する静的な人が必要になりますが、重要なのは、静的メソッドへの依存関係がその 1 つの場所 (プロセスのメイン メソッドになる可能性が高い) に限定されるということです。コードベース全体に分散させるのではなく、アプリケーションのブートストラップのようなものです)。
他のヒント
このような単純な問題に対する創造的な解決策がたくさんあるのは嬉しいですね ;-)
OPの質問からの顕著な事実:
- 接続文字列はコマンドラインで渡されます
- 他の多くのコンポーネントでは接続文字列を使用する必要がある場合があります
それで、 静的要素の使用を回避する方法はありません;それがグローバル変数 (実際には、囲んでいるクラスがないと .NET で実行するのは困難です)、静的クラス、またはシングルトンであるかどうかは、実際には問題ではありません。最短パスの解決策は、コマンド ラインを処理する Program クラスによって初期化される静的クラスです。
他のすべてのソリューションでは、渡された接続文字列への静的アクセスが必要になります。, ただし、これを 1 つ以上の間接層の背後に隠す可能性があります。
基本的なソリューションを派手なもので飾りたくないと言っているわけではありませんが、その必要はありませんし、接続文字列の基本的に静的/グローバルな性質を排除するものではありません。 説明どおり.
保存しているのが文字列だけであれば (おそらく他のグローバル アプリケーション設定も一緒に)、それらすべてを保持するための多数のプロパティを持つ静的クラスを使用するだけです。シングルトンはコードとメンテナンスの作業が多くなりますが、この場合はプロパティ クラスが何も実行しないため、不必要な作業になります。ただ物を握るだけ。
確かに方法はあります。まず最初に、スピードアップしてください 依存性注入 および同様のテクニックについて読んでください。 サービス層 それがここで必要なものだからです。
サービス層に関する限り、ある種の構成サービスが必要です。これは、アプリケーションの一部が構成情報 (接続文字列、URI など) をクエリするモジュールになります。
interface IConfigurationService
string ConnectionString
bool IntegratedSecurity
bool EncryptProfiles
のインスタンスが 1 つだけであることは非常に簡単です。 IConfigurationService
ただし、これはシングルトン パターンではなく、選択した DI コンテナーで実現されます。
次に、すべての DAL サービスは、 IConfigurationService
:
class FooDal : IFooDal
IConfigurationService ConfigurationService
彼らが使えるようになりました IConfigurationService.ConnectionString
接続文字列を取得します。
場合によります。
シングルトンについては次のことに注意してください。
- クラスのインスタンスが 1 つだけ存在するように強制します。
- グローバルアクセスを提供します
グローバルはグローバル アクセスのみを提供しますが、インスタンス化の数については保証しません。
そして最後のオプションは、もちろん、ローカル変数を使用することです。つまり、構成情報をパラメーターとしてコンストラクターまたは必要な場所に渡します。
最初の 2 つのうちのどれかを選択するのは、ある程度簡単なはずです。クラスのインスタンスが 2 つ存在すると、大惨事になりますか?おそらくそうではないと思います。独自の構成クラスをインスタンス化して、アプリの 1 つの特定のコンポーネントに個別のオプションを提供したり、単体テストを初期化したりすることが必要な場合があります。
というケースはほとんどありません。 必要 インスタンスが 1 つだけ存在することを保証します。このため、シングルトンよりもグローバルの方が良い選択となる可能性があります。
ローカルかグローバルかの選択はさらに困難です。一般に、グローバルな可変状態は回避するのが最善です。 不変 状態はそれほど問題ではなく、グローバルな可変状態の場合と同じ同期/同時実行/スケーラビリティの問題を引き起こすことはありません。
ただし、多くの場合、それを必要とするコンポーネントに渡すローカル変数として持つ方が望ましい場合があります。これは、DB アクセスが必要なオブジェクトのコンストラクターに渡すだけで手動で行うことも、IoC コンテナーを使用してある程度自動化することもできます。
ただし、いずれの場合でも、グローバルでない場合、コードはより汎用的で移植性が高くなります。クラスやグローバル データに対する隠れた依存関係がある場合、 しなければならない コードが機能するために存在していても、他のプロジェクトで簡単に使用できなくなったり、コードベース全体をリファクタリングしすぎたりした場合に使用できなくなります。
また、理想的にはテストから除外したい外部データが存在しない限り、コードはコンパイルすらできないため、単体テストも難しくなります。
@Antonが言ったように、次のようなものを公開するインターフェイスが必要です
interface IConfigurationService
string ConnectionString
次に、クラスのいずれかが接続文字列を必要とするたびに、構築時に有効な文字列を含む IConfigurationService の実装をそのクラスに提供します。アプリの起動時 (おそらくデータベース アドレスを取得するとき) に実装を作成するための有効な場所を見つけ、それを必要とするクラスに渡す必要があります。
シングルトンやグローバルに比べて大変な作業のように思えるかもしれませんが、これによりコード内の結合が低下するため、再利用性が向上し、単体テストが容易になります。グローバルは (ほとんど) 悪であると自分自身に納得させてしまえば、非常に簡単です:)
前に述べたように、それを行うためのフレームワークを提供する IoC コンテナーがありますが、あなたの場合にはやりすぎかもしれません。また、「魔法の箱」に作業を任せる前に、自分でパターンを使用するのは良いことです。