プロバイダーに依存しない DAL を設計する方法
-
12-09-2019 - |
質問
私は現在、クエリ ビルダー アプリケーションを開発しています。基本的には、SQL の知識がないユーザーがデータベース上でさまざまなクエリ (結合、SELECT、INSERT、UPDATE、DELETE) を定義できるようにする単純なグラフィカル インターフェイスです。.NET 3.5を使用します。私のアプリケーションは複数のデータベースをサポートし、MS-SQL Server、MySQL、Oracle で動作する必要があるため、関連する講義へのヒントやリンクをいただければ幸いです。 プロバイダーに依存しない DAL を設計する方法.
ユーザーは、データベース サーバー (現在のサーバー上のデータベース) を選択し、接続資格情報を入力し、さまざまなテーブルを選択し、(一連のコンボ ボックスを使用して) クエリを定義し、最後にクエリが有効であれば実行します。もちろん、DAL には各 DB プロバイダーのメソッドが必要です。ファクトリーパターンのラインで何かを考えています。
注記:これは単純な学校のプロジェクトなので、結果として得られるクエリのセキュリティやパフォーマンスには興味がありません。
アップデート: さらに調査を行った後、皆様からいただいた非常に貴重な意見をもとに、 DbProviderFactory
. 。ORM は興味深いかもしれませんが、クエリ アナライザー/ビルダーが必要なだけなので、クエリ アナライザー/ビルダーを使用する意味がわかりません。したがって、使用方法に関する詳細なチュートリアルを教えていただければ幸いです DbProviderFactory
および関連するクラス。
解決
を使用することをお勧めします System.Data.Common.DbProviderFactories
class を使用して汎用 ADO.NET クラスを生成します。
サポートしたいデータベース用の .NET プロバイダーがさらに見つかったら、そのプロバイダー DLL をアプリのパスにドロップし、そのプロバイダーへの参照を追加します。 DbProviderFactory
のプロバイダーの app.config
ファイル。使用するプロバイダーをユーザーに選択させることができます。
これは、というトピックに関する MSDN 記事です。 を取得する DbProviderFactory
(ADO.NET)
私は以前にこのアプローチを使用したことがあり、わずかな構成変更で同じプロジェクト内で MSSQL と SQLite をサポートすることができました。
ただし、クエリビルダーアプリでも同様に機能するかどうかはわかりません...
他のヒント
かなり複雑なクエリを視覚的に編集するのは難しいと言わざるを得ません。 とても 面倒。また、ユーザーがビジュアル デザイナーを使用してデータを挿入/削除できるようにすることは、自分自身を苦しめる確実な方法です。Management Studio のサイズダウン バージョンでは、基本的な SQL の知識に加えて、制限されたサーバー ユーザーがあれば、より適切な作業を行うことができます。
このアプリをまだデザインしたい場合は、NHibernate が必要です。より正確に、 基準クエリ これらは必要なものにかなり近いものをマッピングしているため、機能します。
驚かれるかもしれませんが、プロバイダーに依存しない非常にシンプルな DAL は、従来のシンプルな方法で実現できます。 DataSet
そして DataTable
.
ADO.NET Entity Framework (.NET 3.5 SP1 以降で利用可能) は、エンティティ SQL 言語を使用してデータベースに依存する SQL をほぼ抽象化するため、素晴らしい選択肢だと思います。
ほとんどの ORM (オブジェクト リレーショナル マッパー) は、さまざまな種類のデータベースと通信する方法を知っています。
ユーザーが独自のクエリを作成できるようにすることについては、次のようになります。これには十分注意する必要があります。ユーザーが悪意のあるクエリを作成する可能性があるというほどではありません (ただし、それが問題になる可能性はあります)。利用可能なすべてのサーバー リソースを使用し、データベースに対して効果的なサービス拒否を作成するクエリを作成するのは、驚くほど簡単です。
これがあなたの探求に役立つかどうかはわかりませんが、私が最近学び、心に留めたことの 1 つは、データ モデルの一意の識別子の実装をデータ層の外側に直接伝播させるのではなく、抽象化でラップすることです。たとえば、モデルの識別子をラップするインターフェイスは次のとおりです。
public interface IModelIdentifier<T> where T : class
{
/// <summary>
/// A string representation of the domain the model originated from.
/// </summary>
string Origin { get; }
/// <summary>
/// The model instance identifier for the model object that this
/// <see cref="IModelIdentifier{T}"/> refers to. Typically, this
/// is a database key, file name, or some other unique identifier.
/// <typeparam name="KeyDataType">The expected data type of the
/// identifier.</typeparam>
/// </summary>
KeyDataType GetKey<KeyDataType>();
/// <summary>
/// Performs an equality check on the two model identifiers and
/// returns <c>true</c> if they are equal; otherwise <c>false</c>
/// is returned. All implementations must also override the equal operator.
/// </summary>
/// <param name="obj">The identifier to compare against.</param>
/// <returns><c>true</c> if the identifiers are equal; otherwise
/// <c>false</c> is returned.</returns>
bool Equals(IModelIdentifier<T> obj);
}
過去に渡された可能性のあるビジネス ロジック層 int
s が一意の識別子 (たとえば、データベース テーブルの ID 列から) として渡されるようになりました。
public IPerson RetrievePerson(IModelIdentifier<IPerson> personId)
{
/// Retrieval logic here...
}
データ層には、実装するクラスが含まれます。 IModelIdentifier<Person>
そして、その内部データ型に物理モデルの一意の識別子を設定します。これにより、ビジネス層がデータ層で行われる可能性のある変更 (データ層の置き換えなど) から隔離されます。 int
キー識別子と Guid
s.