在使用存储库的工作单元模式中注入依赖项
本文关键字:模式 注入 依赖 单元 工作 存储 | 更新日期: 2023-09-27 18:09:46
我想创建一个工作类单元,以类似的方式封装存储库。
我遇到的问题是试图通过用IRepository接口替换示例中的通用存储库来实现依赖注入。在链接文章的下面,他们使用getter来检查存储库是否实例化,如果没有,则实例化它。
public GenericRepository<Department> DepartmentRepository
{
get
{
if (this.departmentRepository == null)
{
this.departmentRepository = new GenericRepository<Department>(context);
}
return departmentRepository;
}
}
是强耦合的
我有两种方法。
- 使用构造函数注入。
- 使用setter注入
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
必须向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
实例,并等待Submit()
对数据库的任何更改。
你可以装饰你的UnitOfWork来处理事务等。在这一点上,我将听从强大的。net junkie的建议,并建议您在这里和这里阅读这两篇文章。
不要注入存储库实例,而是注入负责创建这些实例的单个工厂对象。然后你的getter会使用这个factory。
我的解决方案是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>();
}
}