46

これと同様の方法でリポジトリをラップする作業単位クラスを作成したいと考えています。

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

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

これは強く結合しています。

これには 2 つの方法があります。

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

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

2 の問題は、設定を忘れやすく、null 参照例外が発生することです。

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

私は依存性注入を始めたばかりで、このトピックで見つけることができるすべての調査を行いましたが、何か重要なものが欠けている可能性があります。

4

3 に答える 3

66

これにアプローチする方法は、コンテナ注入によってUnitOfWorkそれぞれを作成する責任を負うのではなく、インスタンス化時にその存在を確実に認識できるようにすることです。RepositoryRepositoryUnitOfWork

これにより、

  • 新しいものごとに変更するUnitOfWork必要はありませんRepository
  • あなたはサービスロケーターを使用していません(多くの人がアンチパターンであると考えています)

これはいくつかのコードで最もよく示されています - 私はSimpleInjectorを使用しているので、例はこれに基づいています:

Repository抽象化から始めます:

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();
}

それぞれが自分自身を に登録するRepository 必要UnitOfWorkがあります。これは、抽象親クラスGenericRepositoryを変更して確実に行われるようにすることで実行できます。

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

各実数Repositoryは から継承しますGenericRepository:

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の を認識し、すべてのリポジトリが定義されていることをコンパイル時に検証できます。あなたのコードは拡張用に公開されていますが、変更用には閉じられています

これらすべてをテストするには、これらのクラスを追加します

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());

最後に、次のコンソール テスト コードを実行します。

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待機しているアクティブなインスタンスが 1 つあることがわかります。Submit()

トランザクションなどを処理するように UnitOfWork をデコレートできます。この時点では強力な .NetJunkie に任せます。ここここの 2 つの記事を読むことをお勧めします。

于 2013-04-18T14:36:09.877 に答える
5

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

于 2013-04-18T08:30:48.827 に答える