我正在尝试为我的 C#/.NET 存储库实现工作单元模式 (UoW)。我的计划是将 UoW 作为存储库的参数。这是一个如何使用它的示例:
using (var uow = new UnitOfWork())
{
var itemRepository = new ItemRepository(uow);
itemRepository.Add(new Item());
uow.Commit();
}
此外,对于简单的操作(当不需要事务时),存储库应该能够在没有工作单元的情况下使用:
var itemRepository = new ItemRepository();
var item = itemRepository.Get(itemId);
UnitOfWork/Repository 可以从 ConnectionFactory 获取数据库连接。连接工厂通过依赖注入接收连接选项。但是,这是我的问题:存储库如何获取对 ConnectionFactory 实例的引用?
存储库是手动创建的,因此它们不能通过构造函数注入依赖项。一种选择是拥有存储库工厂,可以注入其依赖项。在这种情况下,用法可能是这样的:
using (var uow = new UnitOfWork())
{
var itemRepository = itemRepositoryFactory.Create(uow);
itemRepository.Add(new Item());
uow.Commit();
}
该解决方案的缺点是每个存储库都需要自己的工厂,而且数量相当多。有没有其他解决方案可以解决这个问题?
我肯定会将 UOW 注册为作用域依赖项。
作用域依赖项在创建它们的容器的生命周期内有效。通常,框架会从父容器生成子容器以执行某些工作。例如,ASP.NET Core 为请求生成一个子容器,然后在请求完成时将其释放。这意味着被注入的 UOW 实例与整个对象图中仅针对该请求的实例是相同的。
如果需要,您还可以创建自己的范围。例如,我已经这样做了两次:
这就是使用 Microsoft 的 DI 框架实现此目标的方法:
var collection = new ServiceCollection();
collection.AddScoped<IUnitOfWork, UnitOfWork>();
var provider = collection.BuildServiceProvider();
var puow = provider.GetRequiredService<IUnitOfWork>();
for(int i = 0; i < 2; i++)
{
//Create the new scope
using var childContainer = provider.CreateScope();
//IUnitOfWork will be a singleton instance in this scope.
var c1uow = childContainer.ServiceProvider.GetRequiredService<IUnitOfWork>();
var c2uow = childContainer.ServiceProvider.GetRequiredService<IUnitOfWork>();
// This should true, since they come from the same scope.
var sameScope = c1uow == c2uow;
//With the requested IUnitOfWork from provider instead, it would be a different instance
//Therefore, this should be false
var diffScope = puow == c1uow;
}
这将允许您简单地将 IUnitOfWork 注入到每个存储库中,而无需为每个存储库创建一个工厂。
如果您在 ASP.NET Core 中编写应用程序,那么这将是开箱即用的。只需将依赖项注册为范围,就像这样
collection.AddScoped<IUnitOfWork, UnitOfWork>();
然后将其注入到您需要的存储库中。您不必担心创建新范围,因为框架会为应用程序收到的每个 http 请求执行此操作。
我强烈推荐阅读 Mark Seemann 的书《依赖注入原则、实践和模式》。它确实深入探讨了依赖注入是什么及其工作原理。不仅如此,我发现他是一位伟大的作家。