就職の面接中に、私はリポジトリパターンがEntity FrameworkのようなORMと連携するのに適したパターンではない理由を説明するように求められました。これはなぜですか?
就職の面接中に、私はリポジトリパターンがEntity FrameworkのようなORMと連携するのに適したパターンではない理由を説明するように求められました。これはなぜですか?
回答:
RepositoryパターンがEntity Frameworkで機能しない理由は見当たりません。リポジトリパターンは、データアクセス層に配置する抽象化層です。データアクセス層は、純粋なADO.NETストアドプロシージャからEntity FrameworkまたはXMLファイルまで何でもかまいません。
さまざまなソース(データベース/ XML / Webサービス)からのデータがある大規模システムでは、抽象化レイヤーを使用すると便利です。このシナリオでは、リポジトリパターンが適切に機能します。Entity Frameworkは、舞台裏で行われていることを隠すのに十分な抽象化だとは思いません。
Entity FrameworkのRepositoryパターンをデータアクセスレイヤーメソッドとして使用しましたが、まだ問題に直面しています。
DbContext
リポジトリで抽象化するもう1つの利点は、単体テスト可能性です。IRepository
インターフェースには2つの実装があります。1つ(実際のリポジトリ)DbContext
はデータベースとの通信に使用し、2つ目はFakeRepository
メモリ内オブジェクト/模擬データを返すことができます。これにより、IRepository
単体テストが可能になるため、を使用するコードの他の部分がテスト可能になりますIRepository
。
public interface IRepository
{
IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
private YourDbContext db;
private EFRepository()
{
db = new YourDbContext();
}
public IEnumerable<CustomerDto> GetCustomers()
{
return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
}
}
public MockRepository : IRepository
{
public IEnumerable<CustomerDto> GetCustomers()
{
// to do : return a mock list of Customers
// Or you may even use a mocking framework like Moq
}
}
DIを使用して、実装を取得します
public class SomeService
{
IRepository repo;
public SomeService(IRepository repo)
{
this.repo = repo;
}
public void SomeMethod()
{
//use this.repo as needed
}
}
Entity Frameworkでリポジトリパターンを使用しない唯一の最良の理由は?Entity Frameworkはすでにリポジトリパターンを実装しています。DbContext
UoW(作業単位)であり、それぞれDbSet
がリポジトリです。この上に別のレイヤーを実装すると、冗長になるだけでなく、メンテナンスが難しくなります。
人々はパターンの目的を理解せずにパターンに従います。リポジトリパターンの場合、目的は低レベルのデータベースクエリロジックを抽象化することです。コードにSQLステートメントを実際に記述する昔、リポジトリパターンは、コードベース全体に散在する個々のメソッドからそのSQLを移動し、1か所でローカライズする方法でした。Entity FrameworkやNHibernateなどのORMを使用することは、このコード抽象化の代わりになるため、パターンの必要性がなくなります。
ただし、ORMの上に抽象化を作成することは悪い考えではなく、UoW / repostitoryほど複雑なものではありません。データがEntity Framework、NHibernate、またはWeb APIからのものかどうかを知らず、気にせずにアプリケーションが使用できるAPIを構築するサービスパターンを使用します。サービスクラスにメソッドを追加するだけでアプリケーションに必要なデータが返されるため、これははるかに簡単です。たとえば、To-doアプリを書いている場合、今週中に期限切れでまだ完了していないアイテムを返すサービスコールがあります。アプリが知っているのは、この情報が必要な場合、そのメソッドを呼び出すことだけです。そのメソッド内および一般的なサービスでは、Entity Frameworkまたは使用している他のものとやり取りします。その後、後でORMを切り替えるか、Web APIから情報を取得することにした場合、
これはリポジトリパターンを使用するための潜在的な議論のように聞こえるかもしれませんが、ここでの重要な違いは、サービスがより薄いレイヤーであり、倉庫。
DbContext
(msdn.microsoft.com/en-us/data/dn314429.aspxを参照)。それよりも低いバージョンでも、iterfaceを実装しているため、モックされたsで偽のDbContext
ようなクラスを使用できます。DbSet
DbSet
IDbSet
Ayende Rahien:運命の穴での建築:リポジトリ抽象化レイヤーの悪
彼の結論に同意するかどうかはまだわかりません。それはキャッチ-22です-一方では、クエリ固有のデータ取得方法でタイプ固有のリポジトリにEFコンテキストをラップすると、実際にはコードで(ある種の)ユニットテストを行うことができますが、これはエンティティではほとんど不可能ですフレームワークのみ。一方、リレーションシップの豊富なクエリとセマンティックメンテナンスを実行する機能を失います(ただし、これらの機能に完全にアクセスできる場合でも、EFや他のORMの周りの卵の殻を歩いているように常に感じます、IQueryable実装がサポートしているメソッドとサポートしていないメソッド、ナビゲーションプロパティコレクションへの追加を作成または単なる関連付けとして解釈するかどうか、遅延ロードまたはイージーロードするかどうかデフォルトなど 多分これは良いことです。ゼロインピーダンスのオブジェクトリレーショナル「マッピング」は神話上の生き物のようなものです。おそらく、Entity Frameworkの最新リリースが「マジックユニコーン」とコードネームされた理由でしょう。
ただし、クエリ固有のデータ取得方法を使用してエンティティを取得すると、ユニットテストは基本的にホワイトボックステストになり、テスト対象のユニットがどのリポジトリメソッドを使用するかを正確に事前に知る必要があるため、この問題に選択肢はありませんそれをモックするために呼び出します。また、統合テストも作成しない限り、クエリ自体を実際にテストしているわけではありません。
これらは複雑なソリューションを必要とする複雑な問題です。すべてのエンティティは、それらの間に関係のない個別のタイプであり、それぞれを独自のリポジトリにアトマイズするふりをするだけでは修正できません。できますが、それはひどいです。
更新: Entity FrameworkのEffortプロバイダーを使用して、ある程度成功しました。Effortは、実際のデータベースに対して使用するのとまったく同じ方法でEFをテストで使用できるようにするインメモリプロバイダー(オープンソース)です。私はこのプロバイダーを使用するために取り組んでいるこのプロジェクトのすべてのテストを切り替えることを検討しています。それは私がこれまでに見つけた唯一の解決策であり、以前に暴言していたすべての問題に対処しています。唯一のことは、メモリ内データベースを作成するため、テストを開始するときにわずかな遅延があることです(NMemoryと呼ばれる別のパッケージを使用してこれを行います)が、これは本当の問題とは思わない。テストにEffort(SQL CEに対して)を使用することについて説明しているコードプロジェクトの記事があります。
DbContext
。今では完全にモックできます。とにかく、いつでもモックすることができますDbSet
。とにかく、それがEntity Frameworkの中核です。プロパティ(リポジトリ)を1つの場所(作業単位)に格納DbContext
するクラスに過ぎませんDbSet
。特に、すべてのデータベースの初期化と接続が必要とされないユニットテストコンテキストでは特にそうです。
おそらくそれを行う理由は、それが少し冗長だからです。Entity Frameworkは、豊富なコーディングと機能的な利点を提供します。そのため、それを使用する理由は、それを利用して、それらの利点を捨てているリポジトリパターンにラップする場合、他のデータアクセスレイヤーを使用することもできます。
理論的には、データベース接続ロジックをカプセル化してより簡単に再利用できるようにすることは理にかなっていると思いますが、以下のリンクが主張するように、現在のフレームワークは基本的にこれを処理します。
ISessionFactory
とISession
簡単にmockableある)、それが持つことは簡単ではありませんDbContext
、残念ながら...
リポジトリパターンを使用する非常に良い理由は、System.Data.EntityからビジネスロジックやUIを分離できるようにすることです。これには、FakesまたはMocksの使用を許可することによる単体テストでの実際の利点など、多くの利点があります。
タイプごとにリポジトリをnew()するIoCコンテナー(たとえば、それぞれDBContextから独自のIDbSetを呼び出すUserRepositoryおよびGroupRepositoryインスタンス)がリクエストごとに複数のコンテキストを引き起こす可能性がある場合、重複するが異なるEntity Framework DbContextインスタンスで問題が発生しました(MVC / Webコンテキスト内)。
ほとんどの場合はまだ機能しますが、その上にサービスレイヤーを追加し、それらのサービスが1つのコンテキストで作成されたオブジェクトが別のコンテキストの新しいオブジェクトに子コレクションとして正しくアタッチされると想定すると、失敗する場合がありますコミットの速度に依存します。
小さなプロジェクトでリポジトリパターンを試した後、使用しないことを強くお勧めします。それはシステムを複雑にするからではなく、データのモックが悪夢だからではなく、テストが役に立たなくなるからです!!
データのモックにより、ヘッダーなしで詳細を追加したり、データベースの制約に違反するレコードを追加したり、データベースが削除を拒否するエンティティを削除したりできます。現実の世界では、1回の更新が複数のテーブル、ログ、履歴、概要など、および最終変更日フィールド、自動生成キー、計算フィールドなどの列に影響する場合があります。
要するに、実際のデータベースでテストを実行すると、実際の結果が得られ、サービスとインターフェイスだけでなく、データベースの動作もテストできます。ストアドプロシージャがデータに対して正しい処理を実行するか、期待される結果を返すか、削除するために送信したレコードが本当に削除されたかを確認できます。このようなテストは、ストアドプロシージャからエラーを発生させることを忘れるなどの問題や、数千のそのようなシナリオも明らかにする可能性があります。
エンティティフレームワークは、これまでに読んだ記事のどれよりもリポジトリパターンを実装しており、彼らが達成しようとしていることをはるかに超えていると思います。
リポジトリは、XBase、AdoX、およびAdo.Netを使用していて、エンティティを使用していた当時のベストプラクティスでした!! (リポジトリ上のリポジトリ)
最後に、リポジトリパターンの学習と実装に多くの時間を費やしている人が多すぎると思います。主に、彼らが自分の時間を無駄にしなかったことを自分自身に証明するために。