this と同様の方法でリポジトリをラップする作業単位クラスを作成したいと思います。
私が抱えている問題は、例の汎用リポジトリーをIRepositoryインターフェースに置き換えることにより、依存性注入を実装しようとしていることです。リンクされた記事のuowでは、ゲッターを使用してリポジトリがインスタンス化されているかどうかを確認し、インスタンス化されていない場合はそれをインスタンス化します。
public GenericRepository<Department> DepartmentRepository
{
get
{
if (this.departmentRepository == null)
{
this.departmentRepository = new GenericRepository<Department>(context);
}
return departmentRepository;
}
}
これは強く結びついています。
これを回避する方法は2つあります。
1の問題は、すべてのリポジトリを注入する場合、特定の作業単位インスタンスで使用しなくても、各リポジトリをインスタンス化する必要があることです。したがって、そうすることでオーバーヘッドが発生します。私は、データベース全体の作業単位クラスを1つ使用することを想像していたので、多くの不必要なインスタンス化と巨大なコンストラクターにつながりました。
2の問題は、設定するのを忘れて、null参照例外が発生してしまうことです。
このシナリオには何らかのベストプラクティスがありますか?そして、私が見逃した他のオプションはありますか?
私は依存関係の注入にたどり着き、このトピックで見つけることができるすべての研究を行っていますが、何か重要なことを見逃している可能性があります。
これにアプローチする方法は、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
mustは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つの記事 here および here を読むことをお勧めします。
リポジトリインスタンスを注入する代わりに、それらのインスタンスの作成を担当する単一のファクトリオブジェクトを注入します。ゲッターはそのファクトリを使用します。
私のソリューションは、まだリポジトリの作成を担当するUnitOfWorkですが、それを行うためにUnitOfWorkでGetRepository()ファクトリメソッドを作成しました。
public interface IUnitOfWork : IDisposable
{
T GetRepository<T>() where T : class;
void Save();
}
public class UnitOfWork : IUnitOfWork
{
private Model1 db;
public UnitOfWork() : this(new Model1()) { }
public UnitOfWork(TSRModel1 dbContext)
{
db = dbContext;
}
public T GetRepository<T>() where T : class
{
var result = (T)Activator.CreateInstance(typeof(T), db);
if (result != null)
{
return result;
}
return null;
}
public void Save()
{
db.SaveChanges();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
public class TestRepository : GenericRepository<Test>, ITestRepository
{
public TestRepository(Model1 db)
: base(db)
{
}
}
public class TestManager: ITestManager
{
private IUnitOfWork unitOfWork;
private ITestRepository testRepository;
public TestManager(IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
testRepository = unitOfWork.GetRepository<TestRepository>();
}
}