リポジトリパターンが最新のORM(EF、nHibernate)で過剰すぎる場合、より優れた抽象化は何ですか?


12

私は最近、エンティティフレームワークのような強力な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

私は、それ自体が設計されていない「過度に複雑な」リポジトリパターンアプローチの明確な代替物や代替物を見つけていません。


4
具体的に何を達成しようとしていますか?抽象化には目的が必要です。CRUDアプリを書いている場合、ORMはおそらく十分に抽象的です。
ジャックB

@JacquesB私は、堅牢なドメインモデルでオブジェクトリレーショナルインピーダンスの問題を回避しようとしていますが、それをmvc実装のビューモデルから遠ざけています。
AnotherDeveloper

リードCospeyはここIQuerableについて言うべき肯定的なものをたくさん持っている:stackoverflow.com/questions/1578778/using-iqueryable-with-linq これは、トランスポート層の方が良いです意味します。抽象化に関しては、EntityTypeを挿入する必要があるが、一般的なメソッドを維持したい場合に、Generic Repositoryパターンの使用が適切に機能することがわかりました。一方、私自身はMSDN LINQフォーラムで、EFはすべてメモリ内にあるためリポジトリパターンであると主張しました。あるプロジェクトでは、メソッド呼び出しとして多数のWhere句を使用して、うまく機能しました。
ジョンピーターズ

しかし、私が検討したが拒否した分野の1つは、Expression Treeビジネスでした。一部は本当に気に入っていますが、...それが役立つと思う前に一生懸命勉強する必要があります。
ジョンピーターズ

2
コントローラーからSQLを呼び出すことに戻る必要があります
-bobek

回答:


12

リポジトリと汎用リポジトリを統合していると思います。

基本的なリポジトリは、データストアをインターフェイスするだけで、データを返すメソッドを提供します

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を非表示にすることです。


1
ORMのORM?私は面白くしようとしていた。ORMを抽象化する必要があるのはなぜですか?
ジョニー

1
あなたが何かを抽象化するのと同じ理由。独自のクラスを使用してコード汚染を回避するために
ユアン・

6

誤って言及している引数のほとんどは、リポジトリパターン機能の属性ではありません。

概念的には、DDDで最初に定義されたリポジトリは、検索または追加できるオブジェクトの単なるコレクションです。その背後にある永続化メカニズムは抽象化されているため、消費者としては、それがメモリ内コレクションであるという錯覚を覚えます。

漏れやすい抽象化(IQueryablesたとえば、公開)を含むリポジトリ実装は、貧弱なリポジトリ実装です。

単なるコレクション操作(作業単位機能など)を公開するリポジトリ実装は、貧弱なリポジトリ実装です。

データアクセス用のリポジトリの代替手段はありますか?はい。ただし、質問で取り上げた問題とは関係ありません。



1

私にとって、リポジトリは、ORMまたは他のDB永続層と組み合わせて、次のような欠点があります。

  1. 作業単位の隠蔽。UoWはプログラマーがコーディングする必要があり、UoW境界と場合によってはコミットポイントを定義せずに、ユーザーがクエリと変更を行うだけのバックグラウンドで、一種の魔法として実装されることはほとんどありません。時々、UoWは、各リポジトリアクセス方法でマイクロUoW(NHibernateセッションなど)に減らすことによって放棄されます。
  2. 永続的な無知を隠蔽するか、最悪の場合、破壊する:「Load()」、「Get()」、「Save()」または「Update()」などのメソッドは、個々のオブジェクトを送信するかのように、即時の単一オブジェクト操作を提案しますSQL / DML、またはファイルを操作するかのように。実際、たとえば、これらの紛らわしい名前を持つNHibernateメソッドは、通常、個別のアクセスを行わず、遅延ロードまたは挿入/更新バッチ(永続性無視)のためにキューに入れます。プログラマーは、なぜ即座にDB操作を取得せず、永続性の無知を強制的に解体せず、パフォーマンスを低下させ、システムを実際に(大いに!)悪化させるために大きな努力を払うのか疑問に思うことがあります。
  3. 制御されない成長。単純なリポジトリには、特定のニーズに合わせてますます多くのメソッドが蓄積される場合があります。

といった:

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、一種のカップリング問題...)。


4
リポジトリパターンを使用しない場合、これらすべての方法はどこに行きますか?
ジョニー


1

Repository-interfaceの目的がunittest(=独立したテスト)のためにデータベースを模擬することである場合、最も簡単な抽象化は簡単に模擬できるものです。

IQueryableの結果に基づいたリポジトリインターフェイスをモックすることは困難です。

単体テストの観点から

IRepository {
   List<Data> GetDataById(string id);
}

簡単にm笑できる

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
}

モックがクエリパラメータの内容を無視する場合にのみ簡単にモックできます。

IGenericRepository<T> {
    IQueryable<T> Get<T>(some_parameters);
}

簡単にm笑できない


0

クエリにラムダ関数を使用する場合、リポジトリパターンが過剰であるとは思わない。特に、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; }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.