web-dev-qa-db-ja.com

リポジトリパターンを正しく使用する方法

リポジトリをどのようにグループ化すればよいのでしょうか。私がasp.net mvcや私の本で見た例のように、基本的にデータベーステーブルごとに1つのリポジトリを使用します。しかし、それは多くのリポジトリのように思われ、後でモックなどのために多くのリポジトリを呼び出さなければなりません。

だから私はそれらをグループ化すべきだと思います。ただし、どのようにグループ化するかはわかりません。

現在、私はすべての登録情報を処理する登録リポジトリを作成しました。しかし、更新する必要があるテーブルが4つあり、その前に3つのリポジトリを更新する必要がありました。

たとえば、テーブルの1つはライセンステーブルです。彼らが登録するとき、私は彼らのキーを見て、それがデータベースに存在するかどうかを確認します。登録以外の場所でこのライセンスキーまたはそのテーブルの他の何かを確認する必要がある場合はどうなりますか?

1つのスポットはログインです(キーの有効期限が切れていないか確認してください)。

それで、私はこの状況で何をしますか?コードをもう一度書き直しますか(DRYを解除)?これら2つのリポジトリを一緒に流用し、他の時点ではメソッドが不要であることを期待してください(おそらく、userNameが使用されているかどうかをチェックするメソッドがあるかもしれません-おそらくどこかでそれが必要になるでしょう)。

また、それらをマージすると、サイトの2つの異なる部分のすべてのロジックを保持するのは長くなり、ValidateLogin()、ValdiateRegistrationForm()などの名前を付ける必要があるため、同じリポジトリに移動する2つのサービスレイヤーが必要になります。 、ValdiateLoginRetrievePassword()など.

またはとにかくリポジトリを呼び出して、奇妙に聞こえる名前を持っていますか?

アプリケーションの多くの場所で使用できるように十分に一般的な名前のリポジトリを作成するのは難しいように思えますが、それでも意味があり、リポジトリ内の別のリポジトリを呼び出すのは良い習慣ではないと思いますか?

86
chobo2

リポジトリパターンをいじったときに間違ったことの1つ-あなたと同じように、テーブルはリポジトリ1:1に関連していると思いました。ドメインドリブンデザインのルールを適用すると、リポジトリのグループ化の問題が解消されることがよくあります。

リポジトリは Aggregate root であり、テーブルではありません。それは-エンティティが単独で生きてはいけない場合(つまり-Registrantが特定のRegistrationに参加している場合)-それは単なるエンティティであり、リポジトリは必要ありません。それが属している集約ルートのリポジトリを介して更新/作成/取得される.

もちろん、多くの場合、リポジトリの数を減らすこの手法(実際には-ドメインモデルを構造化する手法の1つです)は適用できません。これは、すべてのエンティティが集約ルート(ドメインに大きく依存する)であることになっているためです。私は盲目の推測のみ提供できます)。あなたの例では-Licenseは、Registrationエンティティのコンテキストなしでチェックできる必要があるため、集約ルートのようです。

しかし、これはリポジトリをカスケードすることに限定しません(Registrationリポジトリは、必要に応じてLicenseリポジトリを参照できます)。これは、Licenseオブジェクトから直接Registrationリポジトリ(推奨-IoCを介して)を参照することを制限するものではありません。

テクノロジーがもたらす複雑さや、何かを誤解することでデザインを動かさないようにしてください。 2つのリポジトリを構築したくないという理由だけでリポジトリをServiceXにグループ化することは、良い考えではありません。

より適切な名前を付けることをお勧めします-RegistrationServiceつまり.

しかし、サービスは一般に避けられるべきです-それらはしばしば 貧血ドメインモデル につながる原因です。

編集:
IoCの使用を開始してください。依存関係の注入の痛みを本当に緩和します。
書く代わりに:

var registrationService = new RegistrationService(new RegistrationRepository(),  
      new LicenseRepository(), new GodOnlyKnowsWhatElseThatServiceNeeds());

あなたは書くことができるでしょう:

var registrationService = IoC.Resolve<IRegistrationService>();

追伸いわゆる common service locator を使用する方が良いでしょうが、これは単なる例です。

41
Arnis Lapsa

これに対処するために私が始めた1つのことは、N個のリポジトリをラップするサービスを実際に開発することです。うまくいけば、DIまたはIoCフレームワークがそれを容易にするのに役立つことができます。

public class ServiceImpl {
    public ServiceImpl(IRepo1 repo1, IRepo2 repo2...) { }
}

それは理にかなっていますか?また、この邸宅でのサービスといえば、実際にはDDDの原則に準拠している場合と準拠していない場合があることは理解しています。

5
neouser99

私がやっていることは、次のように定義された抽象基本クラスを持っていることです:

public abstract class ReadOnlyRepository<T,V>
{
     V Find(T lookupKey);
}

public abstract class InsertRepository<T>
{
     void Add(T entityToSave);
}

public abstract class UpdateRepository<T,V>
{
     V Update(T entityToUpdate);
}

public abstract class DeleteRepository<T>
{
     void Delete(T entityToDelete);
}

次に、たとえば、一般的な引数が異なる限り、抽象基本クラスからリポジトリを派生させ、単一のリポジトリを拡張できます。

public class RegistrationRepository: ReadOnlyRepository<int, IRegistrationItem>,
                                     ReadOnlyRepository<string, IRegistrationItem> 

等....

一部のリポジトリには制限があり、これにより最大限の柔軟性が得られるため、個別のリポジトリが必要です。お役に立てれば。

2
Michael Mann

私はこれを私のリポジトリクラスとして持っています。そうすれば、テーブル/エリアリポジトリで拡張しますが、それでもDRYを壊さなければならないことがあります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MvcRepository
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected System.Data.Linq.DataContext _dataContextFactory;

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

        public IQueryable<T> FindAll(Func<T, bool> exp)
        {
            return GetTable.Where<T>(exp).AsQueryable();
        }

        public T Single(Func<T, bool> exp)
        {
            return GetTable.Single(exp);
        }

        public virtual void MarkForDeletion(T entity)
        {
            _dataContextFactory.GetTable<T>().DeleteOnSubmit(entity);
        }

        public virtual T CreateInstance()
        {
            T entity = Activator.CreateInstance<T>();
            GetTable.InsertOnSubmit(entity);
            return entity;
        }

        public void SaveAll()
        {
            _dataContextFactory.SubmitChanges();
        }

        public Repository(System.Data.Linq.DataContext dataContextFactory)
        {
            _dataContextFactory = dataContextFactory;
        }

        public System.Data.Linq.Table<T> GetTable
        {
            get { return _dataContextFactory.GetTable<T>(); }
        }

    }
}

[〜#〜]編集[〜#〜]

public class AdminRepository<T> : Repository<T> where T: class
{
    static AdminDataContext dc = new AdminDataContext(System.Configuration.ConfigurationManager.ConnectionStrings["MY_ConnectionString"].ConnectionString);

    public AdminRepository()
        : base( dc )
    {
    }

Linq2SQL.dbmlクラスを使用して作成されたdatacontextもあります。

これで、AllやFindなどの標準の呼び出しを実装する標準のリポジトリができ、AdminRepositoryには特定の呼び出しがあります。

DRYの質問には答えませんが、私は思いません。

2
griegs

リポジトリパターンは悪い設計パターンです。私は多くの古い.Netプロジェクトで作業しており、このパターンは通常、「分散トランザクション」、「部分的なロールバック」、および「接続プールの枯渇」エラーを引き起こし、回避できました。問題は、パターンが内部で接続とトランザクションを処理しようとするが、それらはコントローラー層で処理する必要があることです。また、EntityFrameworkはすでに多くのロジックを抽象化しています。代わりにServiceパターンを使用して共有コードを再利用することをお勧めします。

1
ColacX

ここに例があります FluentNHibernateを使用した一般的なリポジトリの実装。マッパーを作成したクラスを永続化することができます。マッパークラスに基づいてデータベースを生成することもできます。

1
James Jones

Sharp Architecture をご覧になることをお勧めします。エンティティごとに1つのリポジトリを使用することをお勧めします。私は現在、自分のプロジェクトで使用していますが、結果に満足しています。

0
Sly