一种解决方法是不让
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);
}
}
每个真实的
仓库
都继承自
通用仓库
:
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
实例准备好并等待将任何更改提交到数据库。
你可以装饰你的 UnitOfWork 来处理事务等。在这一点上,我会推荐你阅读这两篇文章
here 和
here,并听从强大的 .NetJunkie 的建议。
Lazy<T>
会有什么问题呢?这样只有在实际使用时才会加载。我知道并非所有的 IoC 容器都原生支持Lazy
。 - Nick FreemanLazy
几乎没有任何开销。 - Nick Freeman