Generic Repositoryには本当に利点がありますか?


28

新しいアプリ用の汎用リポジトリを作成する利点に関するいくつかの記事を読んでいました()。同じリポジトリを使用して、複数の異なるエンティティタイプに対して複数の処理を一度に実行できるため、このアイデアは素晴らしいようです。

IRepository repo = new EfRepository(); // Would normally pass through IOC into constructor 
var c1 = new Country() { Name = "United States", CountryCode = "US" };
var c2 = new Country() { Name = "Canada", CountryCode = "CA" };
var c3 = new Country() { Name = "Mexico", CountryCode = "MX" };
var p1 = new Province() { Country = c1, Name = "Alabama", Abbreviation = "AL" };
var p2 = new Province() { Country = c1, Name = "Alaska", Abbreviation = "AK" };
var p3 = new Province() { Country = c2, Name = "Alberta", Abbreviation = "AB" };
repo.Add<Country>(c1);
repo.Add<Country>(c2);
repo.Add<Country>(c3);
repo.Add<Province>(p1);
repo.Add<Province>(p2);
repo.Add<Province>(p3);
repo.Save();

ただし、リポジトリの残りの実装は、Linqに大きく依存しています。

IQueryable<T> Query();
IList<T> Find(Expression<Func<T,bool>> predicate);
T Get(Expression<Func<T,bool>> predicate);
T First(Expression<Func<T,bool>> predicate);
//... and so on

このリポジトリパターンはEntity Frameworkで非常に効果的であり、DbContext / DbSetで使用可能なメソッドの1対1マッピングを提供しました。しかし、Entity Framework以外のデータアクセステクノロジーに対するLinqの採用が遅いことを考えると、DbContextを直接操作することよりもどのような利点がありますか?

リポジトリのPetaPocoバージョンを作成しようとしましたが、PetaPocoはLinq式をサポートしていないため、基本的なGetAll、GetById、Add、Update、Delete、およびSaveにのみ使用しない限り、汎用IRepositoryインターフェイスの作成はほとんど役に立ちません。メソッドを作成し、基本クラスとして使用します。次に、以前に述語として渡すことができるすべての「where」節を処理するために、特殊なメソッドを使用して特定のリポジトリを作成する必要があります。

Generic Repositoryパターンは、Entity Framework以外の何かに役立ちますか?そうでない場合、Entity Frameworkを直接操作するのではなく、なぜ誰かがそれを使用するのでしょうか?


元のリンクは、サンプルコードで使用していたパターンを反映していません。こちらが(更新されたリンク)です。



1
質問は本当に「ジェネリックリポジトリのアドバンテージ」についてですか、それとも「ジェネリックリポジトリインターフェースの背後にある複雑なクエリを非表示にする方法」ですか?インターフェイスと使用方法がlinqに依存する場合、ジェネリックにすることは可能ですか?Repositoryapiには、検索手法とは完全に独立したQueryByExampleメソッドがあり、実装を変更できます。
k3b

「同じリポジトリを使用して、いくつかの異なるエンティティタイプに対していくつかのことを行うことができます」 =>それとも、私またはあなたは汎用リポジトリとは何かを誤解していますか(言及された記事に関するものを含む)?私には、一般的なリポジトリは常に単一ですべてのリポジトリをテンプレート化を意味していたクラスの単一のリポジトリ持っていない、またはインタフェースのインスタンス何でもそのタイプのすべてのエンティティの永続性を管理する...
guillaume31

回答:


35

一般的なリポジトリは、Entity Frameworkにとっても役に立たない(そして私見も悪い)。既に提供されているものIDbSet<T>(これは汎用リポジトリーです)に付加価値はありません。

既に説明したように、汎用リポジトリを他のデータアクセステクノロジの実装に置き換えることができるという議論は、独自のLinqプロバイダーの作成を要求する可能性があるため、かなり弱いです。

簡略化された単体テストに関する2番目の一般的な論点も間違っています。これは、モックリポジトリ/メモリ内データストレージで設定すると、Linqプロバイダーが別の機能を持つ別のプロバイダーに置き換えられるためです。Linq-to-entitiesプロバイダーは、Linq機能のサブセットのみをサポートしIQueryable<T>ます。インターフェイスで利用可能なすべてのメソッドをサポートするわけでもありません。データアクセスレイヤーとビジネスロジックレイヤー間で式ツリーを共有すると、データアクセスレイヤーの偽造を防止できます。クエリロジックを分離する必要があります。

強力な「汎用」抽象化が必要な場合は、他のパターンも含める必要があります。この場合、抽象クエリ言語を使用する必要があります。抽象言語は、リポジトリによって、使用されるデータアクセスレイヤーでサポートされている特定のクエリ言語に変換できます。これは、仕様パターンによって処理されます。Linq on IQueryableは仕様です(ただし、翻訳にはプロバイダー-または式ツリーをクエリに変換するカスタムビジターが必要です)が、独自の簡易バージョンを定義して使用できます。たとえば、NHibernateはCriteria APIを使用します。それでも最も簡単な方法は、特定の方法で特定のリポジトリを使用することです。この方法は、クエリロジックが抽象化の背後に完全に隠されて分離されているため、実装が最も簡単で、テストが最も簡単で、単体テストでの偽造が最も簡単です。


簡単な質問ですが、NHibernateにはISession一般的なユニットテストの目的で簡単にモックできるものがあり、EFで作業するときに「リポジトリ」を喜んで削除しますが、それを簡単に再現する方法はありますか?似て何かISessionISessionFactoryまったくありません、「原因IDbContext私の知る限り...
はPatrykĆwiek

いいえ、ありませんIDbContext。必要なIDbContext場合は、作成して、それを派生コンテキストに実装するだけです。
ラディスラフMrnka

したがって、日常のMVCアプリでは、IDbSet <T>は、リポジトリパターンを完全に忘れるのに十分な汎用リポジトリを提供する必要があると言っています。MVCプロジェクトでDAL参照が許可されていない場合など、特別な状況を除きます。
ProfK

1
いい答えだ。一部の開発者は、理由なしに抽象化の別のレイヤーを作成します。IMO、EFは汎用リポジトリも作業ユニットも必要としません(これらは
組み込みです

1
IDbSet <T>は、Entity Frameworkへの依存関係を持つ汎用リポジトリであり、Entity Frameworkへの依存関係を抽象化するために汎用リポジトリを使用する目的を無効にします。
ジョエルマクベス

7

問題はリポジトリパターンではありません。データの取得と取得方法を抽象化するのは良いことです。

ここでの問題は実装です。任意の式がフィルタリングに機能すると仮定すると、せいぜい危険です。

すべてのオブジェクトに対してリポジトリを直接機能させることは、ポイントを逃します。データオブジェクトがビジネスオブジェクトに直接マップされることはほとんどありません。これらの状況では、Tをフィルターに渡すことはあまり意味がありません。そして、そのような多くの機能を提供することで、別のプロバイダーが登場すると、そのすべてをサポートすることができなくなります。


したがって、GetById(int id)、SortedList()などの特定のメソッドを使用して、データオブジェクトごと、または密接に関連するオブジェクトのグループごとにリポジトリを作成する方が理にかなっていますか?リポジトリからデータオブジェクトのリストを返すのですか、それとも必要なフィールドを持つビジネスオブジェクトに変換するのですか?ちょっと、それがサービス/ビジネス層で起こったことだと思いました。
サム

1
@sam-より具体的なリポジトリを好む傾向があります、はい。翻訳を行っているかどうかは、どこで物事が変わる可能性があるかにかかっています。ドメインが適切に定義されている場合、ドメインの形で物を返します。データが適切に定義されていれば、データ構造の形で物を返します。どちらでもない場合は、明確に定義された基盤として機能するエンティティがあり、それを基に構築してから適応します。
テラスティン

1
一般的なものを一般的なリポジトリに委任する特定のリポジトリを使用できない理由はありませんが、より複雑な呼び出しのための追加のリポジトリ固有のメソッドも使用できます。私はそれが数回そのように行われるのを見てきました。
エリックキング

@EricKingもあり、そこは良かったのですが、データの保存方法の共通性のために一般的なものだけが存在する傾向があるため、抽象化が漏れる傾向があります(たとえば、GetByIDにはIDを持つテーブルが必要です)。
テラスティン

@Telastynはい、私はそれに同意しますが、特定のリポジトリでも起こります。漏れやすい抽象化は、汎用リポジトリーに固有のものではありません。(うわー、それ以上不器用に言い表すことができますか?)
エリックキング

2

汎用データレイヤー(リポジトリは特定の種類のデータレイヤーです)の値により、呼び出し元のコードにほとんどまたはまったく影響を与えずに、コードが基になるストレージメカニズムを変更できます。

理論的には、これはうまく機能します。実際、あなたが観察したように、抽象化はしばしば漏れやすいものです。一方のデータにアクセスするために使用されるメカニズムは、もう一方のメカニズムとは異なります。場合によっては、ビジネスレイヤーで1回、データレイヤーでそれを繰り返すという2回のコードを書くことになります。

汎用データレイヤーを作成する最も効果的な方法は、アプリケーションが事前に使用するさまざまな種類のデータソースを知ることです。これまで見てきたように、LINQまたはSQLがユニバーサルであると仮定することは問題になる可能性があります。新しいデータストアを後付けしようとすると、書き換えが発生する可能性があります。

[編集:以下を追加しました。]

また、アプリケーションがデータ層から何を必要としているかにも依存します。アプリケーションがオブジェクトをロードまたは格納しているだけの場合、データレイヤーは非常に単純です。検索、並べ替え、フィルターの必要性が高まると、データレイヤーの複雑さが増し、抽象化が漏れやすくなります(問題のLINQクエリを公開するなど)。ただし、ユーザーが独自のクエリを提供できるようになったら、データレイヤーのコスト/メリットを慎重に検討する必要があります。


1

ほとんどの場合、データベースの上にコード層を置くことは価値があります。私は通常、上記のコードでは「GetByXXXXX」パターンを好むでしょう。UIをデータインターフェイスの乱雑さから解放しながら、必要に応じて背後のクエリを最適化できます。

ジェネリック医薬品を活用することは間違いなく公正なゲームLoad<T>(int id)です。メソッドを持つことは非常に理にかなっています。しかし、LINQの周りにリポジトリを構築することは、タイプセーフティを少し追加してSQLクエリをあらゆる場所にドロップするのと同じ2010年代のことです。


0

さて、提供されたリンクを使用すると、それがの便利なラッパーであることがわかりますが、DataServiceContextコード操作を減らしたり、可読性を改善したりすることはありません。また、へのアクセスDataServiceQuery<T>が妨げられ、柔軟性が.Where()とに制限されます.Single()。またAddRange()、代替手段も提供されていません。NORれるDelete(Predicate)有用である可能性が提供(repo.Delete<Person>( p => p.Name=="Joe" );ジョー-Sを削除します)。等。

結論:このようなAPIはネイティブAPIを妨害し、いくつかの単純な操作に制限します。


あなたは正しいです。サンプルコードにあるパターンを使用した記事ではありませんでした。私が家に帰ると、リンクを見つけようとします。
サム

質問の最後に追加されたリンクを更新しました。
サム

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.