[英]Dependency injection in unit of work pattern using repositories
I want to create a unit of work class that wraps around repositories in a similar way to this . 我想创建一个环绕在类似的方式对存储库工作类的单元此 。
The problem I'm having is trying to implement dependency injection by replacing the generic repositories in the example with an IRepository interface. 我遇到的问题是尝试通过用IRepository接口替换示例中的通用存储库来实现依赖注入。 In the uow in the linked article they use getters to check if the repository is instantiated and if it isn't then instantiate it.
在链接文章中的uow中,他们使用getter来检查存储库是否被实例化,如果不是,则实例化它。
public GenericRepository<Department> DepartmentRepository
{
get
{
if (this.departmentRepository == null)
{
this.departmentRepository = new GenericRepository<Department>(context);
}
return departmentRepository;
}
}
Which is strongly coupled. 这是强烈耦合的。
I can see two ways around this. 我可以通过两种方式看到这一点。
The problem with 1 is that if I inject all the repositories I have to instantiate each repository even if I don't use them in that particular unit of work instance. 1的问题是,如果我注入所有存储库,我必须实例化每个存储库,即使我不在特定的工作单元实例中使用它们。 Thus incurring the overhead of doing so.
因此招致了这样做的开销。 I was imagining using one, database-wide, unit of work class so this would lead to a lot of needless instantiating and a gigantic constructor.
我想象使用一个数据库范围的工作单元,这会导致很多不必要的实例化和一个巨大的构造函数。
The problem with 2 is that it would be easy to forget to set and end up with null reference exceptions. 2的问题是很容易忘记设置和结束空引用异常。
Is there any sort of best practices in this scenario? 在这种情况下是否有任何最佳实践? And are there any other options I have missed?
还有其他我错过的选择吗?
I'm just getting in to dependency injection and have done all the research I can find on the topic but I may be missing something key. 我刚刚进入依赖注入并完成了我可以在这个主题上找到的所有研究,但我可能会遗漏一些关键。
A way to approach this is to not make the UnitOfWork
responsible for creating each Repository
through Container injection, but instead to make it the responsibility of each Repository
to ensure that the UnitOfWork
knows of its existence upon instantiation. 解决这个问题的方法是不要让
UnitOfWork
负责通过Container注入创建每个Repository
,而是让每个Repository
负责确保UnitOfWork
在实例化时知道它的存在。
This will ensure that 这将确保这一点
UnitOfWork
doesn't need to change for each new Repository
UnitOfWork
不需要为每个新Repository
进行更改 This is best demonstrated with some code - I use SimpleInjector so the examples are based around this: 一些代码最好地证明了这一点 - 我使用SimpleInjector,因此示例基于此:
Starting with the Repository
abstraction: 从
Repository
抽象开始:
public interface IRepository
{
void Submit();
}
public interface IRepository<T> :IRepository where T : class { }
public abstract class GenericRepository<T> : IRepository<T> where T : class { }
and the UnitOfWork
和
UnitOfWork
public interface IUnitOfWork
{
void Register(IRepository repository);
void Commit();
}
Each Repository
must register itself with the UnitOfWork
and this can be done by changing the abstract parent class GenericRepository
to ensure it is done: 每个
Repository
必须使用UnitOfWork
注册自己,这可以通过更改抽象父类GenericRepository
来完成,以确保完成:
public abstract class GenericRepository<T> : IRepository<T> where T : class
{
public GenericRepository(IUnitOfWork unitOfWork)
{
unitOfWork.Register(this);
}
}
Each real Repository
inherits from the GenericRepository
: 每个真实的
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) { }
}
Add in the physical implementation of UnitOfWork
and you're all set: 添加
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());
}
}
The container registration can be set up to automatically pick up all the defined instances of IRepository
and register them with a lifetime scope to ensure they all survive for the lifetime of your transaction: 容器注册可以设置为自动获取所有已定义的
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);
}
}
With these abstractions and an architecture built around DI you have a UnitOfWork
that knows of all Repository
's that have been instantiated within any service call and you have compile time validation that all of your repositories have been defined. 通过这些抽象和围绕DI构建的体系结构,您有一个
UnitOfWork
,它知道已在任何服务调用中实例化的所有Repository
,并且您具有编译时验证,即已定义了所有存储库。 Your code is open for extension but closed for modification . 您的代码已打开以进行扩展,但已关闭以进行修改
To test all this - add these classes 要测试所有这些 - 添加这些类
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();
}
}
Add these lines to BootStrapper.Configure()
将这些行添加到
BootStrapper.Configure()
//register the test classes
container.Register<SomeActivity>();
container.Register<MainActivity>();
Put a break-point against the line of code: 对代码行设一个断点:
_repositories.ToList().ForEach(x => x.Value.Submit());
And finally, run this Console test code: 最后,运行此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();
}
}
}
You'll find the code stops at the break point and you have one active instance of a IRepository
ready and waiting to Submit()
any changes to the database. 您将发现代码在断点处停止,并且您已准备好一个
IRepository
活动实例,并等待Submit()
对数据库的任何更改。
You can decorate your UnitOfWork to handle transactions etc. I will defer to the mighty .NetJunkie at this point and recommend you read these two articles here and here . 您可以装饰您的UnitOfWork以处理交易等。此时我会尊重强大的.NetJunkie,并建议您在此处和此处阅读这两篇文章。
Instead of injecting repository instances inject single factory object which will be responsible for creating those instances. 注入单个工厂对象而不是注入存储库实例,这将负责创建这些实例。 Your getters will then use that factory.
然后,您的吸气剂将使用该工厂。
My solution is UnitOfWork still responsible for creating Repository but I made a GetRepository() factory method in UnitOfWork to do that. 我的解决方案是UnitOfWork仍负责创建Repository,但我在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>();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.