これにアプローチする方法は、コンテナ注入によってUnitOfWork
それぞれを作成する責任を負うのではなく、インスタンス化時にその存在を確実に認識できるようにすることです。Repository
Repository
UnitOfWork
これにより、
- 新しいものごとに変更する
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 つの記事を読むことをお勧めします。