Entity Framework コンテキストの抽象化
プロジェクトのサイズ、スコープ、要件によっては、Entity Framework などの ORM をこれ以上抽象化やカプセル化せずに使用することを選択した場合もあります。大規模なエンタープライズアプリケーションでは、複雑な階層化とサービスが必要になる場合がありますが、開発のオーバーヘッドは正当化されるべきです。
プロジェクトのサイズ、スコープ、要件によっては、Entity Framework などの ORM をこれ以上抽象化やカプセル化せずに使用することを選択した場合もあります。大規模なエンタープライズアプリケーションでは、複雑な階層化とサービスが必要になる場合がありますが、開発のオーバーヘッドは正当化されるべきです。
ORMによって提供されるデータアクセスとオブジェクトリレーショナルの抽象化に加えて、より多くの抽象化をいつ使用するかについて講義する代わりに、私は共通の問題、つまり抽象化が既存のプロジェクトで必要になる問題に対処したいと思います。
この記事では主に Entity Framework について説明しますが、その多くは他の ORM にも適用できます。私のアプローチは、書き直しを避け、コードへの影響を最小限に抑えることです。
どこにでもリポジトリ
一部の人々は、ASP.NET MVCコントローラー、MVVM ビュー モデル、またはプレゼンターで EF コンテキストを使用することさえ認めません。フォーラムやカンファレンスのディスカッションでそのことについて言及すると、特定の特効薬パターンであるリポジトリを使わないことを恥ずかしく思うでしょう。
細かいパターンで、すでに使っているでしょう。ただし、そのユーザーが好むインターフェース(IRepositoryなど)は使用していません。一部のバージョンでは、大量の書き換えが発生します。スプリント 1、2、3 の「リファクタリング」へようこそ。
この結果を回避するには、リポジトリ パターンが何を表しているかを考慮してください。
「リポジトリは、ドメインとデータマッピングレイヤーの間を仲介し、メモリ内のドメインオブジェクトコレクションのように機能します」 –EAAカタログのP
リポジトリの呼び出し元は、ある種のコレクションのみを認識しており、リポジトリのデータ ストレージについては気にしないはずです。Entity Framework はデータ マッピングを処理し、データ量が非決定論的である可能性が高いため、リポジトリにはクエリ条件を受け入れる何らかの方法が必要です。
DbSet はリポジトリです。
問題はリポジトリがないということではありません。それはコンクリートカップリングです。代わりに、コンテキスト クラスの DbSet プロパティをIDbSetに変更してください。コンパイル時エラーがある場合は、System.Data.Entity の using 句が含まれていることを確認してください。具象クラスは DbQuery から派生しますが、その追加メソッドのほとんどは IDbSet の拡張メソッドとしても使用できます。それらをスコープに持ち込むと、うまくいきます。
Context Interface
MSDN ドキュメントでは、DbContextをリポジトリとして説明しています。これは正確ですが、アプリケーション固有のコンテキストはリポジトリバッグに似ています。
public class AppContext : DbContext
{
public IDbSet<Department> Departments { get; set; }
public IDbSet<Employee> Employees { get; set; }
public IDbSet<Person> People { get; set; }
}
派生クラスは、処理するさまざまなエンティティ型のプロパティを定義し、呼び出し元はそれらのプロパティ (コンテキスト) を使用します。従業員は、コンテキストよりも便利で読みやすいです。set() を使用します。これは良いことですが、DbContext はリポジトリ バッグ以上のものです。この抽象化の一部は、必要な部分のみを決定し定義することを必要とします。
初期インターフェースは、具象クラスから完全に抽出できます。ただし、DbContext は作業単位パターンも表すため、コンテキストは変更の保存と破棄も行います。データの保存には明示的なメソッド呼び出しが使用され、メソッド呼び出しが行われない場合、変更は破棄可能なパターンを使用して破棄されます。
public interface IAppContext : IDisposable
{
IDbSet<Department> Departments { get; set; }
IDbSet<Employee> Employees { get; set; }
IDbSet<Person> People { get; set; }
int SaveChanges();
Task<int> SaveChangesAsync();
}
public class AppContext : DbContext, IAppContext
{
public IDbSet<Department> Departments { get; set; }
public IDbSet<Employee> Employees { get; set; }
public IDbSet<Person> People { get; set; }
}
AppContext の明示的な宣言を IAppContext に置き換えると、DbContext の依存関係が公開されます。不足している部分をインターフェイスに追加して、クリーンビルドと依存関係の記録を作成します。
次のステップ
この最初のインターフェイス抽出は面白かっただけでなく、さまざまなオブジェクト指向のデザイン パターンを使用したソリューションに柔軟性を提供します。今後の記事で最も重要なタスクである、具体的なコンテキスト依存関係の削除について説明します。