リポジトリパターンを使用せず、ORMをそのまま使用する(EF)


95

私は常にリポジトリパターンを使用していましたが、最新のプロジェクトでは、それの使用と「作業単位」の実装を完璧にできるかどうかを確認したいと思いました。掘り始めると、「本当に必要なのか」という疑問を持ち始めました。

これはすべて、Stackoverflowに関するいくつかのコメントから始まります。AyendeRahienのブログへの投稿への痕跡が2つあります。

これはおそらくいつまでも話される可能性があり、アプリケーションによって異なります。私が知りたいのは、

  1. このアプローチはEntity Frameworkプロジェクトに適していますか?
  2. このアプローチを使用して、ビジネスロジックはまだサービスレイヤー、または拡張メソッドにありますか(以下で説明するように、拡張メソッドはNHibセッションを使用しています)。

これは、拡張メソッドを使用して簡単に実行できます。クリーンでシンプルで再利用可能。

public static IEnumerable GetAll(
    this ISession instance, Expression<Func<T, bool>> where) where T : class
{
    return instance.QueryOver().Where(where).List();
}

このアプローチをNinjectDIとして使用してContext、インターフェイスを作成し、それをコントローラーに挿入する必要がありますか?

回答:


103

私は多くの道をたどり、さまざまなプロジェクトでリポジトリの多くの実装を作成しました...そして私はタオルを捨ててそれをあきらめました、それが理由です。

例外のコーディング

データベースが1つのテクノロジーから別のテクノロジーに変更される可能性を1%の確率でコーディングしますか?あなたのビジネスの将来の状態について考えていて、それが可能であると答えた場合、a)彼らは別のDBテクノロジーへの移行を実行するのに十分な資金を持っている必要があります、またはb)楽しみのためにDBテクノロジーを選択している、またはc )使用することにした最初のテクノロジーで何かがひどく間違っている。

なぜリッチLINQ構文を破棄するのですか?

LINQとEFが開発されたので、オブジェクトグラフを読み取ったり、トラバースしたりすることができます。それと同じ柔軟性をあなたに与えることができるリポジトリーを作成して維持することは、巨大なタスクです。私の経験では、私は私がしましたリポジトリに作成した任意の時間常にどちらかにするために、リポジトリ層にビジネスロジックのリークが発生していましたが、よりパフォーマンスの高いクエリおよび/またはデータベースへのヒット数を減らします。

作成する必要があるクエリのすべての順列に対してメソッドを作成したくありません。ストアドプロシージャを作成することもできます。したくないGetOrderGetOrderWithOrderItemGetOrderWithOrderItemWithOrderActivityGetOrderByUserId、のように...私はちょうどメインエンティティとトラバースを取得したいととして、オブジェクトグラフを含めるIそうしてください。

リポジトリのほとんどの例はでたらめです

あなたがブログや何かのような本当に必要最低限​​のものを開発しているのでなければ、あなたのクエリはリポジトリパターンを取り巻くインターネットで見つけた例の90%ほど単純ではありません。ストレスがたまりません!これは、泥の中をクロールして理解する必要があるものです。作成した完全に考え抜かれたリポジトリ/ソリューションを壊すクエリが常に1つあります。その時点までは、2番目に自分自身を推測し、技術的な負債/侵食が始まります。

私の仲間をユニットテストしないでください

しかし、リポジトリがない場合の単体テストについてはどうでしょうか?どうやってモックするの?簡単です、そうではありません。両方の角度から見てみましょう:

リポジトリなし- DbContextを使用してモックを作成できますが、クエリは実行時に決定されるため、IDbContext実際にはLINQ to EntitiesではなくLINQ to Objectsの単体テストを行っています...わかりました。したがって、これをカバーするのは統合テスト次第です。

リポジトリあり-リポジトリをモックして、その間のレイヤーを単体テストできます。いいでしょう?そうではありません...クエリをより高性能にするため、またはデータベースへのヒットを減らすためにリポジトリレイヤーにロジックをリークする必要がある上記のケースでは、ユニットテストでそれをどのようにカバーできますか?現在はレポレイヤーにあり、IQueryable<T>正しくテストしたくないですか?また、正直に言って、ユニットテストでは20行の.Where()句を含むクエリをカバーしていません。.Include()クエリは実行時に生成されるため、関係の束であり、データベースに再度アクセスして、他のすべてのものを実行します。また、上位層の永続性を無視できるようにリポジトリを作成したため、データベーステクノロジーを変更したい場合は、統合テストに戻って、ユニットテストが実行時に同じ結果を保証することはありません。そのため、リポジトリの全体的なポイントは奇妙に見えます。

2セント

プレーンストアドプロシージャ(一括挿入、一括削除、CTEなど)でEFを使用すると、既に多くの機能と構文が失われていますが、C#でもコーディングしているため、バイナリを入力する必要はありません。EFを使用しているため、さまざまなプロバイダーを使用したり、オブジェクトグラフをさまざまな方法で適切に関連させて操作したりすることができます。特定の抽象化は有用ですが、一部は有用ではありません。


16
それらを単体テストできるようにリポジトリを作成することはありません。ビジネスロジックを単体テストできるようにリポジトリを作成します。クエリが機能することを確認することについては、リポジトリにはロジックのみが含まれ、ビジネスは含まれていないため、リポジトリの統合テストを作成する方がはるかに簡単です。
jgauffin 14

16
Coding for the exception:リポジトリを使用すると、データベースエンジンを切り替えることができなくなります。ビジネスと永続性を分離することです。
jgauffin 14

2
これらはすべて非常に有効なポイントであり、その背後には多くの真実があります。ただし、不足しているのは、一貫した場所に制約されるのではなく、アプリケーションについてLINQがばらまかれているため、コードビハインドページでSQL呼び出しに相当するEFが作成されるという認識です。すべてのLINQクエリは、アプリケーションの潜在的なメンテナンスポイントであり、その数が増えるほど(そして普及するほど)メンテナンスコストとリスクが高くなります。エンティティに「削除済み」フラグを追加し、エンティティがクエリされる大規模なアプリケーション内のすべての場所を特定し、それぞれを変更する必要があることを想像してください
DVK

2
これは近視眼的でぎくしゃくしていると思います。なぜロジックをリポジトリにリークするのですか?もしそうなら、なぜそれが重要なのでしょうか?これはデータの実装です。LINQをレポの後ろに隠して、残りのコードからLINQを隔離しています。あなたはそれをテストしないと言います、しかしそれからあなたはそれをすることに対する議論としてそれをテストすることができないことを使用します。したがって、リポジトリを作成し、IQueryableを公開しないでください。また、テストしないでください。少なくとも、データの実装とは別にすべてをテストできます。そして、データベースの変更の1%の可能性は、$に関しては依然として大きなものです。
Sinaesthetic

5
この回答の+1。Entity Framework Coreのリポジトリは本当に必要ないことがわかりました。DbSetあるリポジトリ、とDbContextある作業の単位。ORMがすでにリポジトリパターンを実装しているのに、なぜそれを実装するのですか?テストするには、プロバイダーをに変更しますInMemory。そしてあなたのテストをしてください!それはMSDNで十分に文書化されています。
Mohammed Noureldin 2017年

49

リポジトリパターンは抽象化です。これは、複雑さを軽減し、コードの残りの部分を無知にすることを目的としています。ボーナスとして、それはあなたが書くことができますユニットテストの代わりに、統合テストを。

問題は、多くの開発者がパターンの目的を理解できず、永続性固有の情報を呼び出し元に漏らす(通常はを公開するIQueryable<T>)リポジトリを作成することです。そうすることで、OR / Mを直接使用するよりもメリットはありません。

別の回答に対処するための更新

例外のコーディング

リポジトリを使用することは、永続化テクノロジーを切り替えることができるということではありません(つまり、データベースの変更や、代わりにWebサービスの使用など)。それは、ビジネスロジックを永続性から分離して、複雑さと結合を減らすことです。

単体テストと統合テスト

リポジトリの単体テストは記述しません。限目。

しかし、リポジトリ(または永続性とビジネスの間にあるその他の抽象化レイヤー)を導入することで、ビジネスロジックの単体テストを作成できます。つまり、データベースの構成が正しくないためにテストが失敗することを心配する必要はありません。

クエリについても。LINQを使用する場合は、リポジトリの場合と同様に、クエリが機能することを確認する必要もあります。これは、統合テストを使用して行われます。

違いは、ビジネスをLINQステートメントと混在させていない場合、失敗しているのは永続コードであり、他の何かではないことを100%確信できるということです。

テストを分析すると、懸念事項が混在していなければ(LINQ +ビジネスロジックなど)、テストがよりクリーンであることがわかります。

リポジトリの例

ほとんどの例はでたらめです。それは本当です。ただし、任意のデザインパターンをググると、多くのくだらない例が見つかります。これは、パターンの使用を避ける理由にはなりません。

正しいリポジトリ実装の構築は非常に簡単です。実際、1つのルールに従うだけで済みます。

必要なときまで、リポジトリクラスに何も追加しないでください。

多くのコーダーは怠惰であり、一般的なリポジトリを作成し、必要となる可能性のある多くのメソッドを持つ基本クラスを使用しようとします。YAGNI。リポジトリクラスを1回記述し、アプリケーションが存続する限り(数年になる可能性があります)保持します。なぜ怠惰であることによってそれをファック。基本クラスを継承せずにクリーンに保ちます。読みやすく、保守しやすくなります。

(上記のステートメントはガイドラインであり、法律ではありません。基本クラスは非常に動機付けられます。追加する前によく考えて、正しい理由で追加するようにしてください)

古いもの

結論:

ビジネスコードにLINQステートメントが含まれていても問題がなく、単体テストに関心がない場合は、Entity Frameworkを直接使用しない理由はないと思います。

更新

私はリポジトリパターンと「抽象化」が本当に意味することの両方についてブログを書いています:http : //blog.gauffin.org/2013/01/repository-pattern-done-right/

アップデート2

20以上のフィールドを持つ単一エンティティタイプの場合、順列の組み合わせをサポートするクエリメソッドをどのように設計しますか?名前だけで検索を制限したくない場合は、ナビゲーションプロパティを使用した検索について、特定の価格コードのアイテムを含むすべての注文を一覧表示する、ナビゲーションプロパティ検索の3レベル。IQueryable発明された全体の理由は、データベースに対する検索の任意の組み合わせを作成できるようにすることでした。理論的にはすべてが素晴らしいように見えますが、ユーザーのニーズは理論よりも優れています。

繰り返しますが、20以上のフィールドを持つエンティティが正しくモデル化されていません。それは神の実体です。それを分解します。

私はそれIQueryableがクェアリングのために作られたのではないことを主張していません。私はそれが漏洩しやすいので、それはリポジトリパターンのような抽象化レイヤーには適切ではないと言っています。100%完全なLINQ To Sqlプロバイダー(EFなど)はありません。

これらはすべて、eager / lazyローディングの使用方法やSQLの "IN"ステートメントの実行方法など、実装固有の機能を備えています。IQueryableリポジトリで公開すると、ユーザーはこれらすべてのことを知る必要があります。したがって、データソースを抽象化しようとする試みはすべて完全な失敗です。OR / Mを直接使用するよりもメリットを得ることなく、複雑さを追加するだけです。

リポジトリパターンを正しく実装するか、まったく使用しないでください。

(大きなエンティティを本当に処理したい場合は、RepositoryパターンをSpecificationパターンと組み合わせることができます。これにより、テストも可能な完全な抽象化が得られます。)


6
IQueryableを公開しないと検索が制限され、人々はさまざまなタイプのクエリに対してGetメソッドをさらに作成することになり、最終的にはリポジトリがより複雑になります。
Akash Kava 2013

3
中心的な問題にまったく対処していません。リポジトリを通じてIQueryableを公開することは、完全な抽象化ではありません。
jgauffin 2013

1
それ自体で実行するために必要なすべてのインフラストラクチャを含むクエリオブジェクトを持つことは、imoへの道です。検索語句であるフィールドを指定すると、結果のリストが返されます。QOの内部では、好きなことができます。そしてそれはインターフェースなので、簡単にテストできます。上記の私の投稿を参照してください。それがベストだ。
h.alex 2013

2
個人的には、基になるセットをメンバーの1つに公開するのではなく、IQueryable <T>インターフェイスをRepositoryクラスに実装することも理にかなっていると思います。
dark_perfect

3
@yat:集約ルートごとに1つのリポジトリ。しかし、私はそれが集計ルートとテーブルの集計ではなく、単に集計ルートと集計です。実際のストレージは1つのテーブルまたはそれらの多くを使用する場合があります。つまり、各集計とテーブル間の1対1のマッピングではない場合があります。リポジトリを使用して、複雑さを軽減し、基盤となるストレージの依存関係を削除します。
jgauffin 2013

27

Repository抽象化と抽象化の両方のIMOは、UnitOfWork意味のある開発において非常に貴重な場所を持っています。人々は実装の詳細について議論しますが、猫のスキンを作成する多くの方法があるように、抽象化を実装する多くの方法があります。

あなたの質問は、具体的に使用するかしないか、そしてその理由です。

Entity Frameworkにこれらの両方のパターンが既に組み込まれていることに気付いたと思いますDbContextが、UnitOfWorkDbSetRepositoryです。それらは単にクラスと基礎となるデータアクセス実装との間を容易にするだけなので、一般的に、UnitOfWorkまたはRepositoryそれら自体を単体テストする必要はありません。サービスのロジックを単体テストするときに、これら2つの抽象概念を模擬する必要があります。

テストを行うロジックとテストされるロジックの間にコードの依存関係の層(制御できない)を追加する外部ライブラリを使用して、モック、偽物などを行うことができます

したがって、マイナーなポイントは、ユニットテストをモックするときに独自の抽象化をUnitOfWork行い、Repository最大限の制御と柔軟性を提供することです。

結構ですが、私にとってこれらの抽象化の本当の力は、アスペクト指向プログラミング手法を適用し、SOLIDの原則に準拠する簡単な方法を提供することです。

だからあなたはあなたを持っていますIRepository

public interface IRepository<T>
    where T : class
{
    T Add(T entity);
    void Delete(T entity);
    IQueryable<T> AsQueryable();
}

そしてその実装:

public class Repository<T> : IRepository<T>
    where T : class
{
    private readonly IDbSet<T> _dbSet;
    public Repository(PPContext context) 
    {
        _dbSet = context.Set<T>();
    }

    public T Add(T entity)
    { 
        return _dbSet.Add(entity); 
    }

    public void Delete(T entity)
    {
        _dbSet.Remove(entity); 
    }

    public IQueryable<T> AsQueryable() 
    {
        return _dbSet.AsQueryable();
    }
}

これまでのところ普通のことは何もありませんが、ここではロギングデコレータを使用して簡単なロギングを追加したいと思います。

public class RepositoryLoggerDecorator<T> : IRepository<T>
    where T : class
{
    Logger logger = LogManager.GetCurrentClassLogger();
    private readonly IRepository<T> _decorated;
    public RepositoryLoggerDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() );
        T added = _decorated.Add(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        return added;
    }

    public void Delete(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        _decorated.Delete(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable();
    }
}

すべて完了し、既存のコードに変更はありません。例外処理、データキャッシング、データ検証など、追加できる他の多くの横断的な懸念があります。設計および構築プロセス全体で、既存のコードを変更せずに単純な機能を追加できる最も価値のあるものです。私たちのあるIRepository抽象化

今度は、StackOverflowでこの質問を何度も目にしました。「マルチテナント環境でEntity Frameworkをどのように機能させるのですか?」

https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant

あなたが持っている場合 Repository抽象化、答えは「デコレータを追加するのは簡単です」です

public class RepositoryTennantFilterDecorator<T> : IRepository<T>
    where T : class
{
    //public for Unit Test example
    public readonly IRepository<T> _decorated;
    public RepositoryTennantFilterDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        return _decorated.Add(entity);
    }

    public void Delete(T entity)
    {
        _decorated.Delete(entity);
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable().Where(o => true);
    }
}

IMOは、常に少数の場所で参照されるサードパーティのコンポーネントに単純な抽象を配置する必要があります。この観点から見ると、ORMはコードの大部分で参照されているため、完璧な候補です。

誰かが「なぜ抽象化する必要があるのか​​(例: Repositoryこれまたはそのサードパーティのライブラリをは、「なぜあなたはそうしないのですか?」

PSデコレーターは、SimpleInjectorなどのIoCコンテナーを使用して簡単に適用できます。

[TestFixture]
public class IRepositoryTesting
{
    [Test]
    public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
    {
        Container container = new Container();
        container.RegisterLifetimeScope<PPContext>();
        container.RegisterOpenGeneric(
            typeof(IRepository<>), 
            typeof(Repository<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryLoggerDecorator<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryTennantFilterDecorator<>));
        container.Verify();

        using (container.BeginLifetimeScope())
        {
            var result = container.GetInstance<IRepository<Image>>();

            Assert.That(
                result, 
                Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>)));
            Assert.That(
                (result as RepositoryTennantFilterDecorator<Image>)._decorated,
                Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>)));
        }
    }
}

11

まず第一に、いくつかの回答で示唆されているように、EF自体はリポジトリパターンです。リポジトリと名付けるだけで、抽象化をさらに作成する必要はありません。

単体テスト用のモカブルリポジトリ、本当に必要なのか?

EFにユニットテストでテストDBと通信させ、SQLテストDBに対してビジネスロジックを直接テストします。リポジトリパターンのモックを作成するメリットはまったくありません。テストデータベースに対してユニットテストを実行するときに実際に何が問題になっていますか?それは一括操作が不可能であるため、生のSQLを作成することになります。メモリ内のSQLiteは、実際のデータベースに対して単体テストを行うのに最適な候補です。

不要な抽象化

将来的に簡単にEFをNHbibernateなどに置き換えられるようにリポジトリを作成しますか?素晴らしい計画に聞こえますが、それは本当に費用効果的ですか?

Linqは単体テストを強制終了しますか?

それがどのように殺すことができるかについての例を見たいと思います。

依存性注入、IoC

これらは素晴らしい言葉です。理論的には素晴らしいように見えますが、優れたデザインと優れたソリューションの間のトレードオフを選択する必要がある場合もあります。私たちはそれをすべて使用しましたが、結局すべてをゴミ箱に捨て、別のアプローチを選択しました。サイズと速度(コードのサイズと開発の速度)は、実際には非常に重要です。ユーザーには柔軟性が必要です。コードがDIまたはIoCの点で優れたデザインであるかどうかは気にしません。

Visual Studioを構築していない限り

多くの人が開発するVisual StudioやEclipseなどの複雑なプログラムを構築していて、高度にカスタマイズ可能である必要がある場合は、これらすべての優れた設計が必要です。これらのIDEが何年にもわたって開発された後、すべての優れた開発パターンが浮かび上がり、これらの優れたデザインパターンすべてが非常に重要な場所で進化しました。ただし、単純なWebベースの給与計算または単純なビジネスアプリを実行している場合は、数百人のユーザーのために構築するために時間を費やすのではなく、時間の経過とともに開発を進化させ、数百人のユーザーに対してのみ展開することをお勧めします。

フィルタービューとしてのリポジトリ-ISecureRepository

一方、リポジトリは、現在のユーザー/ロールに基づいて必要なフィラーを適用することでデータへのアクセスを保護するEFのフィルタービューである必要があります。

しかし、そうすることは、維持するために巨大なコードベースになってしまうため、リポジトリをさらに複雑にします。人々は、異なるユーザータイプまたはエンティティタイプの組み合わせに対して異なるリポジトリを作成することになります。これだけでなく、DTOも多数発生します。

次の回答は、クラスとメソッドのセット全体を作成せずにFiltered Repositoryを実装する例です。質問に直接回答することはできませんが、質問を導き出すのに役立ちます。

免責事項:私はエンティティREST SDKの作成者です。

http://entityrestsdk.codeplex.com

上記を念頭に置いて、CRUD操作のフィルターを保持するSecurityContextに基づいてフィルターされたビューのリポジトリを作成するSDKを開発しました。また、2種類のルールのみが複雑な操作を簡素化します。1つはエンティティへのアクセスで、もう1つはプロパティの読み取り/書き込みルールです。

利点は、さまざまなユーザータイプのビジネスロジックやリポジトリを書き換えるのではなく、単にアクセスをブロックまたは許可するだけです。

public class DefaultSecurityContext : BaseSecurityContext {

  public static DefaultSecurityContext Instance = new DefaultSecurityContext();

  // UserID for currently logged in User
  public static long UserID{
       get{
             return long.Parse( HttpContext.Current.User.Identity.Name );
       }
  }

  public DefaultSecurityContext(){
  }

  protected override void OnCreate(){

        // User can access his own Account only
        var acc = CreateRules<Account>();

        acc.SetRead( y => x=> x.AccountID == UserID ) ;
        acc.SetWrite( y => x=> x.AccountID == UserID );

        // User can only modify AccountName and EmailAddress fields
        acc.SetProperties( SecurityRules.ReadWrite, 
              x => x.AccountName,
              x => x.EmailAddress);

        // User can read AccountType field
        acc.SetProperties<Account>( SecurityRules.Read, 
              x => x.AccountType);

        // User can access his own Orders only
        var order = CreateRules<Order>();
        order.SetRead( y => x => x.CustomerID == UserID );

        // User can modify Order only if OrderStatus is not complete
        order.SetWrite( y => x => x.CustomerID == UserID 
            && x.OrderStatus != "Complete" );

        // User can only modify OrderNotes and OrderStatus
        order.SetProperties( SecurityRules.ReadWrite, 
              x => x.OrderNotes,
              x => x.OrderStatus );

        // User can not delete orders
        order.SetDelete(order.NotSupportedRule);
  }
}

これらのLINQルールは、すべての操作のSaveChangesメソッドでデータベースに対して評価され、これらのルールはデータベースの前でファイアウォールとして機能します。


3
DBに対する単体テストは、テストに対して追加の外部要件があることを意味します。そのDBがダウンしているか、データがクリアされているか、そのDBに何かが起こった場合、テストは失敗します。これは望ましくありません。IQueryableを公開するリポジトリは、セットアップに約2分かかります。ここで時間を無駄にすることはありません。DIに長い時間がかかったのはなぜですか?これにはすべて数分かかります。これはすべて、サービスレイヤーでの複雑なクエリの単体テストに最適でした。接続するためにデータベースを必要としないのはとても良かったです。nugetからモックフレームワークを取得するには、約1分かかりました。この作業には時間がかかりません。
user441521 14年

@ user441521 IQueryableのあるリポジトリ2分でセットアップできますか?あなたが住んでいる世界、私たちのライブサイトでのすべてのasp.netリクエストはミリ秒以内に処理されます。モックや偽造などにより、コードがより複雑になり、時間の浪費になります。ユニットがビジネスロジックユニットとして定義されていない場合、ユニットテストは役に立ちません。
Akash Kava 14年

7

どちらの方法が正しいかについては多くの議論があります。両方とも許容できるので、私はそれを検討します。私が最も好きな方(リポジトリがないUoW)を使用します。

EFでは、UoWはDbContextを介して実装され、DbSetはリポジトリです。

データレイヤーの操作方法については、DbContextオブジェクトを直接操作するだけです。複雑なクエリの場合は、再利用できるクエリの拡張メソッドを作成します。

Ayendeは、CUD操作を抽象化することがいかに悪いかについての投稿もいくつかあると思います。

私は常にインターフェイスを作成し、コンテキストをそれから継承するので、DIのIoCコンテナーを使用できます。


それでは、拡張メソッドはどれほど広範囲なのでしょうか?拡張機能で別のエンティティの状態を取得する必要があるとしましょうか?それが今の私の最大の関心事です。拡張メソッドの例をいくつか示していただけませんか?
Dejan.S 2013年

ayende.com/blog/153473/… およびayende.com/blog/153569/…。(これらはs#arp liteと呼ばれるアーキテクチャー(フレームワーク?)のレビューです。ほとんどが良いですが、彼はリポジトリーとCUDの抽象化に同意しません)。
Josh

NHibernateベース。EFを使用した例はありませんか?また、別のエンティティを呼び出す必要がある場合、静的拡張メソッドでどのように最適化するのですか?
Dejan.S 2013年

3
ドメインオブジェクトのプロパティを、データベースに格納されていないデータでハイドレートする必要があるまでは、これで十分です。または、肥大化したORMよりも高性能なテクノロジーに立ち寄る必要があります。おっとっと!ORMは単にリポジトリの代わりではなく、その実装の詳細です。
cdaq 2013年

2

EFに最も当てはまるのは、リポジトリパターンではありません。これはFacadeパターンです(EFメソッドの呼び出しをよりシンプルで使いやすいバージョンに抽象化しています)。

EFは、リポジトリパターン(および作業単位パターン)を適用するものです。つまり、EFはデータアクセス層を抽象化するものであるため、ユーザーはSQLServerを処理していることを理解できません。

そして、その上で、EF上のほとんどの「リポジトリ」は、EFの単一のメソッドに、非常に簡単に、同じシグネチャを持つポイントにさえマッピングするだけなので、優れたFacadeでさえありません。

次に、このいわゆる「リポジトリ」パターンをEFに適用する2つの理由は、テストを容易にし、それに対する「既定」の呼び出しのサブセットを確立するためです。それ自体は悪くありませんが、明らかにリポジトリではありません。


1

Linqは現在の「リポジトリ」です。

ISession + Linqはすでにリポジトリであり、GetXByYメソッドも必要ありません。QueryData(Query q)一般化。DALの使用に少し偏執的であるため、私はまだリポジトリー・インターフェースを好みます。(保守性の観点からは、特定のデータアクセスインターフェイス上にいくつかのファサードも必要です)。

ここに私たちが使用するリポジトリがあります-それはnhibernateの直接的な使用から切り離しますが、linqインターフェースを提供します(例外的なケースではISessionアクセスとして、最終的にリファクタリングされる可能性があります)。

class Repo
{
    ISession _session; //via ioc
    IQueryable<T> Query()
    {
        return _session.Query<T>();
    }
}

サービス層はどうしますか?
Dejan.S 2013年

コントローラーは読み取り専用データのリポジトリをクエリしますが、なぜ追加のレイヤーを追加するのですか?他の可能性は、ますますサービスレベルリポジトリになる傾向がある "ContentService"を使用することです:GetXByYなど。変更操作の場合-アプリケーションサービスはユースケースの単なる抽象化です
。– mikalai

私はビジネスロジックのサービスレイヤーを行うのに慣れています。ContentServiceを使用して何をフォローしているのか本当にわかりません。詳しく説明してください。ヘルパークラスを「サービスレイヤー」として実行するのは悪い習慣でしょうか?
Dejan.S 2013年

「サービス層」とは、「アプリケーションサービス」を意味しました。彼らは、ドメインレイヤーのリポジトリおよびその他のパブリック部分を使用できます。「サービスレイヤー」は悪い習慣ではありませんが、List <X>の結果を提供するためだけにXServiceクラスを作成することは避けます。コメント欄はサービスを詳細に説明するには短すぎるようです。申し訳ありません。
ミカライ2013年

たとえば、カートの計算で、計算を行うためにアプリケーション設定パラメーターと特定の顧客パラメーターを取得する必要があるとします。これは、アプリケーションのいくつかの場所で再利用されます。その状況にどのように対処しますか?ヘルパークラスまたはアプリケーションサービス?
Dejan.S 2013年

1

現時点では、リポジトリ(または、それを呼び出すことを選択したもの)は、主に永続化レイヤーを抽象化することについてです。

私はそれをクエリオブジェクトと組み合わせて使用​​しているので、アプリケーションで特定のテクノロジとの結合はありません。また、テストが非常に簡単になります。

だから、私は持っている傾向があります

public interface IRepository : IDisposable
{
    void Save<TEntity>(TEntity entity);
    void SaveList<TEntity>(IEnumerable<TEntity> entities);

    void Delete<TEntity>(TEntity entity);
    void DeleteList<TEntity>(IEnumerable<TEntity> entities);

    IList<TEntity> GetAll<TEntity>() where TEntity : class;
    int GetCount<TEntity>() where TEntity : class;

    void StartConversation();
    void EndConversation();

    //if query objects can be self sustaining (i.e. not need additional configuration - think session), there is no need to include this method in the repository.
    TResult ExecuteQuery<TResult>(IQueryObject<TResult> query);
}

デリゲートとしてコールバックを備えた非同期メソッドを追加する可能性があります。リポジトリは一般的に実装するのが簡単なので、アプリからアプリへの実装の行に触れることはできません。ええと、これは少なくともNHを使用する場合には当てはまります。EFでもそれを行いましたが、EFを嫌いにしました。4.会話はトランザクションの開始です。いくつかのクラスがリポジトリインスタンスを共有している場合は非常に便利です。また、NHの場合、私の実装の1つのリポジトリは、最初のリクエストで開かれる1つのセッションに相当します。

次に、クエリオブジェクト

public interface IQueryObject<TResult>
{
    /// <summary>Provides configuration options.</summary>
    /// <remarks>
    /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository.
    /// If not used through a repository, it can be useful as a configuration option.
    /// </remarks>
    void Configure(object parameter);

    /// <summary>Implementation of the query.</summary>
    TResult GetResult();
}

NHで使用する構成では、ISessionを渡すだけです。EFでは多かれ少なかれ意味がありません。

クエリの例は..(NH)

public class GetAll<TEntity> : AbstractQueryObject<IList<TEntity>>
    where TEntity : class
{
    public override IList<TEntity> GetResult()
    {
        return this.Session.CreateCriteria<TEntity>().List<TEntity>();
    }
}

EFクエリを実行するには、セッションではなく、抽象ベースにコンテキストが必要です。もちろん、ifcも同じです。

このようにして、クエリ自体がカプセル化され、簡単にテストできます。何よりも、私のコードはインターフェースのみに依存しています。すべてがとてもきれいです。ドメイン(ビジネス)オブジェクトはそれだけです。たとえば、ほとんどテストできず、ドメインオブジェクトでデータアクセス(クエリ)コードを混合するアクティブレコードパターンを使用する場合など、責任の混合はありません。自体??)。誰もがデータ転送用のPOCOを自由に作成できます。

概して、このアプローチでは、想像できるものを失うことなく、多くのコードの再利用とシンプルさが提供されます。何か案は?

そして、Ayendeの素晴らしい投稿と継続的な献身に感謝します。ここでの彼のアイデア(クエリオブジェクト)は、私のものではありません。


1
永続性エンティティ(POCO)はビジネス/ドメインエンティティではありません。また、リポジトリの目的は、ビジネス(任意の)レイヤーを永続性から切り離すことです。
MikeSW 2013

カップリングが見えません。POCOの部分についてはある程度同意しますが、気にしないでください。「本物の」POCOを使用することを妨げるものはなく、このアプローチを引き続き使用します。
h.alex 2013

1
エンティティは、まったくダムPOCOである必要はありません。実際、ビジネスロジックをエンティティにモデル化することは、DDD群衆が常に行うことです。この開発スタイルは、NHまたはEFと非常によく調和しています。
クリス

1

私にとって、それは比較的少数の要因による単純な決定です。要因は次のとおりです。

  1. リポジトリはドメインクラス用です。
  2. 一部のアプリでは、ドメインクラスは永続性(DAL)クラスと同じですが、一部は異なります。
  3. それらが同じである場合、EFはすでにリポジトリを提供しています。
  4. EFは遅延読み込みとIQueryableを提供します。私はこれらが好き。
  5. EF経由でリポジトリを抽象化/「ファサード」/再実装すると、通常、遅延したIQueryableが失われます

したがって、私のアプリが#2を正当化できず、ドメインモデルとデータモデルを分離できない場合は、通常、私は#5を気にしません。

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