2013-04-17 9 views
31

thisと同様にリポジトリをラップする作業単位クラスを作成したいとします。リポジトリを使用した作業単位パターンへの依存性注入

私が抱えている問題は、例の汎用リポジトリをIRepositoryインターフェイスに置き換えて依存性注入を実装しようとしていることです。リンクされた記事の中で、彼らはゲッターを使ってリポジトリがインスタンス化されているかどうかをチェックし、リポジトリがインスタンス化されていないかどうかをチェックします。

public GenericRepository<Department> DepartmentRepository 
{ 
    get 
    { 
     if (this.departmentRepository == null) 
     { 
      this.departmentRepository = new GenericRepository<Department>(context); 
     } 
     return departmentRepository; 
    } 
} 

強く結合している。

これを回避するには2つの方法があります。

  1. コンストラクタインジェクションを使用します。
  2. セッター注入を使用します。

1の問題は、すべてのリポジトリを注入すると、その特定の作業単位でそれらを使用しなくても、各リポジトリをインスタンス化する必要があるということです。したがって、そうするオーバーヘッドが発生します。私は、データベース全体の作業単位クラスを使用することを想像していたので、これは多くの不必要なインスタンス化と巨大なコンストラクタにつながります。

2の問題は、ヌル参照例外を設定して終了することを忘れやすいことです。

このシナリオでは、ベストプラクティスはありますか?そして私が逃した他のオプションはありますか?

私は依存性注入に取り掛かり、トピックで見つけることができるすべてのリサーチを行っていますが、何かキーが見つからない可能性があります。

+1

コンストラクタインジェクションで['Lazy '](http://msdn.microsoft.com/en-us/library/dd642331.aspx)を使用すると、実際に使用するときにのみ読み込まれる問題がありますそれ?すべてのIoCコンテナが「レイジー」をサポートしているわけではありません。 –

+1

実際にこれに何か利点はありますか?私は、別のオブジェクトの作成のための1つのオブジェクトの作成に代えています。私はリポジトリ(そのコンストラクタが文脈の依存関係を割り当てている)が作成されるときに大きなオーバーヘッドを必要とすることに疑いを持っています。 – rashleighp

+0

はそれが行く方法だとは言っていませんが、コンストラクタインジェクションの問題は各リポジトリをインスタンス化するオーバーヘッドを招いていると言いました。 'レイジー'の作成は事実上何もありません。 –

答えて

41

これにアプローチする方法は、コンテナの注入を介してRepositoryを作成するためのUnitOfWorkが責任を負うことはありませし、代わりにUnitOfWorkは、インスタンス作成時にその存在を知っていることを保証するために、それを各Repositoryの責任を作ることです。

これは

  • は、あなたのUnitOfWorkあなたが
anti-patternであることを多くの人に考えられる)サービスロケータを使用していないそれぞれの新しい Repository
  • のために変更する必要がないことを保証します

    これは最高のいくつかのコードを用いて実証された - 私は例がこの問題を回避基づいてSimpleInjectorを使用します。

    public interface IRepository 
    { 
        void Submit(); 
    } 
    public interface IRepository<T> :IRepository where T : class { } 
    public abstract class GenericRepository<T> : IRepository<T> where T : class { } 
    

    UnitOfWork

    public interface IUnitOfWork 
    { 
        void Register(IRepository repository); 
        void Commit(); 
    } 
    

    は各RepositoryUnitOfWorkに自身を登録する必要があり、これはそれがあることを確認するために、抽象親クラスGenericRepositoryを変更することで行うことができますRepository抽象化で開始行わ:

    public abstract class GenericRepository<T> : IRepository<T> where T : class 
    { 
        public GenericRepository(IUnitOfWork unitOfWork) 
        { 
         unitOfWork.Register(this); 
        } 
    } 
    
    からの

    各実Repository継承:

    public class Department { } 
    public class Student { } 
    
    public class DepartmentRepository : GenericRepository<Department> 
    { 
        public DepartmentRepository(IUnitOfWork unitOfWork): base(unitOfWork) { } 
    } 
    
    public class StudentRepository : GenericRepository<Student> 
    { 
        public StudentRepository(IUnitOfWork unitOfWork) : base(unitOfWork) { } 
    } 
    

    UnitOfWorkの物理的な実装に追加すると、すべてのセットです:

    public class UnitOfWork : IUnitOfWork 
    { 
        private readonly Dictionary<string, IRepository> _repositories; 
        public UnitOfWork() 
        { 
         _repositories = new Dictionary<string, IRepository>(); 
        } 
    
        public void Register(IRepository repository) 
        { 
         _repositories.Add(repository.GetType().Name, repository); 
        } 
    
        public void Commit() 
        { 
         _repositories.ToList().ForEach(x => x.Value.Submit()); 
        } 
    } 
    

    コンテナの登録が自動的にすべてピックアップするように設定することができます定義されたインスタンスIRepositoryを生涯有効範囲に登録して、トランザクションの存続期間中存続するようにします。

    public static class BootStrapper 
    { 
        public static void Configure(Container container) 
        { 
         var lifetimeScope = new LifetimeScopeLifestyle(); 
    
         container.Register<IUnitOfWork, UnitOfWork>(lifetimeScope); 
    
         container.RegisterManyForOpenGeneric(
          typeof(IRepository<>), 
          lifetimeScope, 
          typeof(IRepository<>).Assembly); 
        } 
    } 
    

    DIを中心に構築されたこれらの抽象化とアーキテクチャでは、UnitOfWorkがあり、すべてのサービス呼び出しでインスタンス化されたすべてのRepositoryがあり、すべてのリポジトリが定義されているコンパイル時間の検証があります。あなたのコードはopen for extension but closed for modificationです。

    はすべてこれをテストするために - これらのクラスを追加

    public class SomeActivity 
    { 
        public SomeActivity(IRepository<Department> departments) { } 
    } 
    
    public class MainActivity 
    { 
        private readonly IUnitOfWork _unitOfWork; 
        public MainActivity(IUnitOfWork unitOfWork, SomeActivity activity) 
        { 
         _unitOfWork = unitOfWork; 
        } 
    
        public void test() 
        { 
         _unitOfWork.Commit(); 
        } 
    } 
    

    コードの行に対してブレークポイントを置きBootStrapper.Configure()

    //register the test classes 
    container.Register<SomeActivity>(); 
    container.Register<MainActivity>(); 
    

    にこれらの行を追加します。

    _repositories.ToList().ForEach(x => x.Value.Submit()); 
    

    そして、最後に、このConsoleテストコードを実行してください:

    class Program 
    { 
        static void Main(string[] args) 
        { 
         Container container = new Container(); 
         BootStrapper.Configure(container); 
         container.Verify(); 
         using (container.BeginLifetimeScope()) 
         { 
          MainActivity entryPoint = container.GetInstance<MainActivity>(); 
          entryPoint.test(); 
         } 
        } 
    } 
    

    コード停止はブレークポイントで実行され、アクティブなインスタンスはIRepositoryであり、Submit()はデータベースへの変更を待っています。

    あなたは、トランザクションなどを処理するためにUnitOfWorkを装飾することができます。私は強力に譲歩します。この時点でNetJunkieは、これら2つの記事herehereを読むことをお勧めします。

  • +0

    私はこのアイデアが大好きですが、私は各リポジトリが独自のものを持っているのではなく、作業単位の単一のコンテキストを持つことを目指していました。ここで実装する場合、作業単位はコンテキストをリポジトリに渡す必要があり、リポジトリは作業ユニットに登録する必要があります。彼らは密接に結合しているという意味ではないでしょうか?申し訳ありませんが、私はこれについて間違っている場合、IOCと依存性注入は私には非常に新しいです。 – rashleighp

    +2

    コンテナにコンテキストを登録し、それを 'UnitOfWork'オブジェクトと' Repository'オブジェクトのコンストラクタに注入することができます。上に示した 'lifetimeScope'で' Context'を登録すると、同じインスタンスがすべてのオブジェクトに確実に注入されます。 – qujck

    +0

    @rashleighp私は過去6ヶ月間IoCしか使用していません。一度あなたがそれを取得し、それをセットアップする - 魔法はちょうど起こります。ここで助けてくれる別のリンクがあります:http://www.codeproject.com/Articles/51007/Simple-Injector – qujck

    5

    リポジトリインスタンスを挿入する代わりに、それらのインスタンスの作成を担当する単一のファクトリオブジェクトを挿入します。あなたのゲッターはその工場を使用します。

    +1

    例を挙げてください。 – Krishna

    関連する問題