web-dev-qa-db-ja.com

IRepositoryとは何で、何に使用されますか?

IRepositoryとは何ですか?なぜそれが使用されるのか、簡潔で簡単な例は害になりません。

36
Sevki

MVCは懸念の分離を促進しますが、それはM V Cレベルで止まりません。

データアクセスはそれ自体が懸念事項です。 MVCのMビット、つまりモデルで実行する必要があります。モデルをどのように構築するかはあなた次第ですが、人々は通常、試行錯誤されたパターンに従います(なぜ車輪を再発明するのですか?)。リポジトリパターンは現在の標準です。ただし、単純な式は期待しないでください。ほとんどの場合、バリエーションは開発者と同じくらいです。

IRepositoryは、ユーザーが作成する単なるインターフェイスです(MVCまたはASP.NETまたは.NETの一部ではありません)。リポジトリを実際の実装から「分離」できます。それはあなたのコードを意味するので、デカップリングは良いです...:

  1. コードははるかに再利用可能です。これはまったく良いことです。
  2. コードでは、制御の反転(または依存性注入)を使用できます。これは、懸念事項を十分に区別するのに適しています。これは単体テストを可能にするため、特に優れています...
  3. コードは単体テストできます。これは、複雑なアルゴリズムを持つ大規模プロジェクトで特に役立ちます。使用しているテクノロジと、ソフトウェアでモデル化しようとしているドメインについての理解が深まるため、あらゆる場所で有効です。
  4. コードは、一般的なパターンに従って、ベストプラクティスを中心に構築されます。メンテナンスがずっと簡単になるため、これは良いことです。

したがって、デカップリングを販売したので、あなたの質問に対する答えは、IRepositoryは作成したインターフェイスであり、Repositoriesを継承させるということです。信頼できるクラス階層を使用して作業できます。

通常、一般的なIRepositoryを使用します。

IRepository

TEntityは、エンティティです。私が使用するコードは次のとおりです。

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

namespace Wingspan.Web.Mvc
{
    public interface IRepository<TEntity> where TEntity : class
    {
        List<TEntity> FetchAll();
        IQueryable<TEntity> Query {get;}
        void Add(TEntity entity);
        void Delete(TEntity entity);
        void Save();
    }
}

このインターフェイスの具体的な実装は次のとおりです。

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

using Wingspan.Web.Mvc;

namespace ES.eLearning.Domain
{
    public class SqlRepository<T> : IRepository<T> where T : class
    {
        DataContext db;
        public SqlRepository(DataContext db)
        {
            this.db = db;
        }

        #region IRepository<T> Members

        public IQueryable<T> Query
        {
            get { return db.GetTable<T>(); }
        }

        public List<T> FetchAll()
        {
            return Query.ToList();
        }

        public void Add(T entity)
        {
            db.GetTable<T>().InsertOnSubmit(entity);
        }

        public void Delete(T entity)
        {
            db.GetTable<T>().DeleteOnSubmit(entity);
        }

        public void Save()
        {
            db.SubmitChanges();
        }

        #endregion
    }
}

これにより、次のように記述できます。

SqlRepository<UserCourse> UserCoursesRepository = new SqlRepository<UserCourse>(db);

Dbは、たとえばサービスに挿入されるDataContextインスタンスです。

UserCoursesRepositoryを使用すると、Serviceクラスに次のようなメソッドを記述できます。

public void DeleteUserCourse(int courseId)
        {
            var uc = (UserCoursesRepository.Query.Where(x => x.IdUser == UserId && x.IdCourse == courseId)).Single();
            UserCoursesRepository.Delete(uc);
            UserCoursesRepository.Save();
        }

そして今、私のコントローラーで、私はただ書くことができます:

MyService.DeleteUserCourse(5);
MyService.Save();

このパターンを使用すると、アプリの開発は非常にシンプルなコントローラーに至るアセンブリーラインになります。組立ラインのすべての部分は、他のすべてとは独立してテストできるため、バグは芽に挟まれています。

これが長くて扱いにくい答えである場合、本当の答えは次のとおりです。

スティーブン・サンダーソンの本「Pro ASP.NET MVC 2 Framework」 を購入し、MVCで考えることを学びます。

47
awrigley

IRepositoryは、リポジトリパターンを実装するときに指定するインターフェイスです。 @Brian Ballが述べたように、それは.NETの一部ではなく、あなたが作成するインターフェースです。

リポジトリパターンを使用する開発者は、実装にインターフェイスの使用を広く推奨しています。たとえば、現在開発中のアプリケーションには、5つのリポジトリがあります。 4つの特定および1つのジェネリック。それぞれがIRepositoryから継承しているため、将来的に実装の問題が発生することはありません。

コード例に関しては、次のことを試します。

interface IRepository<T> where T : class {
    IQueryable<T> Select();
}

汎用リポジトリーとして実装:

public class Repository<T> : IRepository<T> where T : class {
    public IQueryable<T> Select() {
        return this.ObjectContext.CreateObjectSet<T>();
    }
}

専用のリポジトリとして実装:

public class EmployeeRepository : IRepository<Employee> {
    public IQueryable<Employee> Select() {
        return this.ObjectContext.Employees;
    }
}

両方のRepository<T>EmployeeRepositoryIRepositoryを実装しますが、クエリの実行方法が少し異なります。汎用リポジトリーは、何かを試みる前にTのオブジェクトセットを作成する必要があります。

それを念頭に置いて Repository<T>はインターフェイスにロックされることになっています。EmployeeRepositoryは、より複雑なロジックを実現するために、より特殊なメソッドを実装できます。

これが少しお役に立てば幸いです。

15
Gup3rSuR4c

IRepositoryは、.Netフレームワークで定義された型ではありません。通常、その名前のインターフェイスが表示されると、プログラムはリポジトリパターン( https://web.archive.org/web/20110503184234/http://blogs.hibernatingrhinos.com/nhibernate/archive/2008/ 10/08/the-repository-pattern.aspx )。一般に、人々がこのパターンを使用すると、すべてのリポジトリが準拠するインターフェイスが作成されます。これには多くの利点があります。利点のいくつかは、コードのデカップリングと単体テストです。

IoC( http://en.wikipedia.org/wiki/Inversion_of_control )で利用できるように、これが行われることも一般的です。

4
Brian Ball

リポジトリはオブジェクトのメモリ内コレクションであるかのように、基礎となる任意のデータストアを表す抽象化です。

この定義は、一般的な慣行とシステムの制限により、より実用的な形式に変形されます基礎となる任意のデータストア、おそらく切断されたものを表すメモリ内のオブジェクトのコレクション。内部では、リポジトリはデータベース、フラットファイル、メモリ内のオブジェクトのコレクション、またはあなたが想像する他のあらゆるものにリンクされます。リポジトリのユーザーは気にしません。

したがって、IRepositoryは、Apiコードがクライアントコードとリポジトリとの対話をどのように望んでいるかを定義するインターフェイスコントラクトです。これには、たとえば、リポジトリコントラクトのこの非常に一般的な例として、追加、更新、削除、および取得コントラクトが含まれます。

_public interface IRepository<TEntity> where TEntity : class
{
    List<TEntity> GetAll();
    void Add(TEntity entity);
    void Delete(TEntity entity);
    void Save();
}
_

しかし、いくつかの理由で別のインターフェイスを使用することを好みます。

まず、通常はリポジトリを単独で使用することはありません。おそらく作業単位パターンで使用するため、リポジトリにはSave()メソッドを使用しないでください。 Update(T entity)メソッドがあるかもしれませんが、なぜですか?リポジトリから受け取るオブジェクトは、オブジェクト自体への参照を取得したため、オブジェクトのあらゆる種類のコレクションから受け取る他のオブジェクトと同様に、自動的に更新/更新されます。 (例:TEntityPersonオブジェクトで、「チャック」という人物を取得し、彼の姓を「Bartowski」から「Carmichael」に変更した場合、リポジトリはおそらくすでに上記のエンティティを更新しました。これがあなたの心の中で希薄に思えるなら、Update(T entity)メソッドを実装しても何も問題はありません。)

第二に、ほとんどのリポジトリは切断された環境を処理できるはずです。ソリューションにこの要件がない場合でも、接続されていないシナリオを処理するインターフェイスを作成し、未実装のままにすることができます。これで、将来の準備が整いました。

最後に、私たちのコントラクトはリポジトリの真の性質により意味があります-メモリ内の任意のデータストアを表すオブジェクトのコレクション、場合によっては切断されたもの

_public interface IRepository<TEntity> where TEntity : class
{

    List<TEntity> GetAll();
    List<TEntity> Get(Func<TEntity, bool> where);
    void Insert(TEntity entity);
    void Insert(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void Remove(IEnumerable<TEntity> entities);

    void SyncDisconnected(TEntity entity, bool forDeletion = false);
    void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}
_

すべてのエンティティの基本クラスを定義する場合、DomainObjectと呼び、Idフィールドを指定して、次のことができます。

_public interface IRepository<TEntity> where TEntity : DomainObject
{
    TEntity GetById(object Id);

    List<TEntity> GetAll();
    List<TEntity> Get(Func<TEntity, bool> where);
    void Insert(TEntity entity);
    void Insert(IEnumerable<TEntity> entities);
    void Remove(TEntity entity);
    void Remove(IEnumerable<TEntity> entities);

    void SyncDisconnected(TEntity entity, bool forDeletion = false);
    void SyncDisconnected(IEnumerable<TEntity> entities, bool forDeletion = false);
}
_

オプションのパラメーターforDeletionが気に入らない場合は、削除されたオブジェクトの同期を許可するメソッドを追加できます。

_    void SyncDisconnectedForDeletion(TEntity entity);
_

これを行う必要がある理由は、ほとんどの場合、切断されたオブジェクトを削除のために同期することは、切断されたオブジェクトを追加または変更のために同期することと互換性がないためです。 (試してみてください。ストアに対する削除の要件は、追加または変更の要件とは大きく異なります)。したがって、インターフェイスはコントラクトを定義して、実装が2つの間を識別できるようにする必要があります。

このインターフェイスは、Entity Frameworkなどの基礎となるデータストアへの他の抽象化を含む、基礎となるあらゆるデータストアの任意のリポジトリ(接続または切断)に対して実装できます。

2
Price Jones