私は最近、Entity Frameworkのような強力なORMでリポジトリパターンを使用することに対する多くの議論を読みました。
単体テストのような状況でパターンを使用することに対するもう1つの論点は、より一般的な実装ではIQueryableを利用するため、リポジトリパターンは漏洩しやすい抽象化であるということです。
リポジトリパターンの使用に反対する議論は私には理にかなっていますが、推奨される抽象化の代替方法は、しばしばより混乱しやすく、問題と同じくらいやり過ぎに見えます。
Jimmy Bogardsのソリューションは、抽象化を吹き飛ばすだけでなく、彼自身のアーキテクチャも導入することの組み合わせのようです。 https://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repositories/
リポジトリが不必要にある別の例....しかし、私のアーキテクチャを使用してください! http://blog.gauffin.org/2012/10/22/griffin-decoupled-the-queries/
別の... http://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework
それ自体が設計されていない「過度に複雑な」リポジトリパターンアプローチの明確な代替または代替案を見つけられませんでした。
リポジトリと汎用リポジトリを融合していると思います。
基本的なリポジトリは、データストアをインターフェースし、データを返すメソッドを提供します
IRepository {
List<Data> GetDataById(string id);
}
IQueryableやその他のランダムクエリを渡す方法を介してコードにデータレイヤーをリークせず、明確に定義されたテスト可能で注入可能なメソッドの表面を提供します。
Generic Repositoryを使用すると、ORMのようにクエリを渡すことができます
IGenericRepository<T> {
List<T> Get<T>(IQuery query);
//or
IQueryable<T> Get<T>();
}
基本的に単なる別の汎用リポジトリーであるORMの上で汎用リポジトリーを使用する意味があまりないことに同意します。
答えは、基本的なリポジトリパターンを使用してORMを非表示にすることです
あなたが誤って言及する引数のほとんどは、それが持っていないリポジトリパターン機能に起因します。
概念的には、DDDで最初に定義されたリポジトリは、検索または追加できるオブジェクトのコレクションにすぎません。その背後にある永続化メカニズムは抽象化されているため、コンシューマとしては、それがメモリ内コレクションであるという錯覚を覚えます。
リークの多い抽象化(たとえば、IQueryables
を公開する)を持つリポジトリ実装は、貧弱なリポジトリ実装です。
単なるコレクション操作(作業ユニット機能など)を公開するリポジトリ実装は、貧弱なリポジトリ実装です。
データアクセスのリポジトリに代わるものはありますか?はい。ただし、質問で取り上げた問題とは関係ありません。
Repository-interfaceの目的がnittest(=単独でのテスト)のためにデータベースを模擬することである場合、最適な抽象化は模倣しやすいものです。
IQueryableの結果に基づくリポジトリインターフェースをモックするのは困難です。
ユニットテストの観点から
IRepository {
List<Data> GetDataById(string id);
}
簡単にあざけることができます
IGenericRepository<T> {
List<T> Get<T>(IQuery query);
}
モックがクエリパラメータのコンテンツを無視する場合にのみ、簡単にモックできます。
IGenericRepository<T> {
IQueryable<T> Get<T>(some_parameters);
}
簡単にあざけることはできません
私にとって、リポジトリは、ORMまたは他のDB永続化レイヤーと組み合わせて、次の欠点があります。
といった:
public interface ICarsRepository /* initial */
{
ICar CreateNewCar();
ICar LoadCar(int id); // bad, should be for multiple IDs.
void SaveCar(ICar carToSave); // bad, no individual saves, use UoW commit!
}
public interface ICarsRepository /* a few years later */
{
ICar CreateNewCar();
ICar LoadCar(int id);
IList<ICar> GetBlueCars();
IList<ICar> GetRedYellowGreenCars();
IList<ICar> GetCarsByColor(Color colorOfCars); // a bit better
IList<ICar> GetCarsByColor(IEnumerable<Color> colorsOfCars); // better!
IList<ICar> GetCarsWithPowerBetween(int hpFrom, int hpTo);
IList<ICar> GetCarsWithPowerKwBetween(int kwFrom, int kwTo);
IList<ICar> GetCarsBuiltBetween(int yearFrom, int yearTo);
IList<ICar> GetCarsBuiltBetween(DateTime from, DateTime to); // some also need month and day
IList<ICar> GetHybridCarsBuiltBetween(DateTime from, DateTime to);
IList<ICar> GetElectricCarsBuiltBetween(DateTime from, DateTime to);
IList<ICar> GetCarsFromManufacturer(IManufacturer carManufacturer);
bool HasCarMeanwhileBeenChangedBySomebodyElseInDb(ICar car); // persistence ignorance broken
void SaveCar(ICar carToSave);
}
4.神の危険オブジェクト:モデルまたはデータアクセス層のすべてをカバーする1つの神クラスを作成したくなるかもしれません。リポジトリクラスには、Carメソッドだけでなく、すべてのエンティティのメソッドが含まれます。
私の意見では、多くの単一目的メソッドの巨大な混乱を避けるために、少なくともいくつかのクエリの機会を提供する方が良いです。 LINQ、独自のクエリ言語、またはORMから直接取得したもの(OK、一種の結合の問題など)であっても関係ありません。
クエリにラムダ関数を使用する場合、リポジトリパターンは過剰だとは思いません。特にORMを抽象化する必要がある場合(私の意見では常にそうするべきです)、リポジトリ自体の実装の詳細は気にしません。
例えば:
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
UserRepository ur = new UserRepository();
var userWithA = ur.GetBy(u => u.Name.StartsWith("A"));
Console.WriteLine(userWithA.Name);
ur.GetAllBy(u => u.Name.StartsWith("M"))
.ForEach(u => Console.WriteLine(u.Name));
ur.GetAllBy(u => u.Age > 13)
.ForEach(u => Console.WriteLine(u.Name));
}
}
public class UserRepository
{
List<User> users = new List<User> {
new User{Name="Joe", Age=10},
new User{Name="Allen", Age=12},
new User{Name="Martin", Age=14},
new User{Name="Mary", Age=15},
new User{Name="Ashton", Age=29}
};
public User GetBy(Predicate<User> userPredicate)
{
return users.Find(userPredicate);
}
public List<User> GetAllBy(Predicate<User> userPredicate)
{
return users.FindAll(userPredicate);
}
}
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}