Che*_*hev 12 .net c# entity-framework unit-of-work repository-pattern
目前,我们已经在工作中实现了存储库模式.我们所有的存储库都位于自己的接口后面,并通过Ninject进行映射.我们的项目非常庞大,我试图解决的这种模式有几个怪癖.
首先,有一些控制器,我们需要在同一个控制器中有超过10到15个存储库.在要求这么多的存储库时,构造函数变得相当丑陋.在多个存储库上调用方法后,第二个怪癖就会显现出来.在使用多个存储库之后,我们需要调用SaveChanges方法,但是我们应该调用哪个存储库?每个存储库都有一个.所有存储库都注入了相同的Entity Framework数据上下文实例,因此选择任何随机存储库来调用save on都可以.它看起来很混乱.
我查看了"工作单元"模式并提出了一个我认为可以解决这两个问题的解决方案,但我对这个解决方案并不是100%有信心.我创建了一个名为的类DataBucket.
// Slimmed down for readability
public class DataBucket
{
private DataContext _dataContext;
public IReportsRepository ReportRepository { get; set; }
public IEmployeeRepository EmployeeRepository { get; set; }
public IDashboardRepository DashboardRepository { get; set; }
public DataBucket(DataContext dataContext,
IReportsRepository reportsRepository,
IEmployeeRepository employeeRepository,
IDashboardRepository dashboardRepository)
{
_dataContext = dataContext;
this.ReportRepository = reportsRepository;
this.EmployeeRepository = employeeRepository;
this.DashboardRepository = dashboardRepository;
}
public void SaveChanges()
{
_dataContext.SaveChanges();
}
}
Run Code Online (Sandbox Code Playgroud)
这似乎解决了这两个问题.现在SaveChanges数据桶本身只有一种方法,你只注入一个对象,数据桶.然后,您将所有存储库作为属性访问.数据桶看起来有点乱,因为它会在构造函数中接受所有(很容易50个或更多)我们的存储库.
添加新存储库的过程现在包括:创建接口,创建存储库,在Ninject中映射接口和存储库,以及向数据存储区添加属性并填充它.
我确实想到了一种替代方案,可以消除上面的一步.
public class DataBucket
{
private DataContext _dataContext;
public IReportsRepository ReportRepository { get; set; }
public IEmployeeRepository EmployeeRepository { get; set; }
public IDashboardRepository DashboardRepository { get; set; }
public DataBucket(DataContext dataContext)
{
_dataContext = dataContext;
this.ReportRepository = new ReportsRepository(dataContext);
this.EmployeeRepository = new EmployeeRepository(dataContext);
this.DashboardRepository = new DashboardRepository(dataContext);
}
public void SaveChanges()
{
_dataContext.SaveChanges();
}
}
Run Code Online (Sandbox Code Playgroud)
这个几乎消除了Ninject中的所有存储库映射,因为它们都在数据桶中实例化.所以现在添加新存储库的步骤包括:创建接口,创建存储库,向数据存储区添加属性和实例化.
你能看到这个型号的任何缺陷吗?从表面上看,以这种方式使用我们的存储库似乎更方便.这是一个以前解决过的问题吗?如果是这样,这个问题最常见和/或最有效的方法是什么?
首先,有一些控制器,我们需要在同一个控制器中有超过10到15个存储库.
向抽象工厂模式问好.而不是在Ninject中注册所有存储库并将它们注入控制器只注册工厂的单个实现,它将能够提供您需要的任何存储库 - 您甚至可以只在控制器真正需要它时才懒惰地创建它们.比将工厂注入控制器.
是的,它也有一些缺点 - 你给予控制器权限来获取任何存储库.这对你有用吗?如果需要,您可以随时为某些子系统创建多个工厂,或者只在单个实现上公开多个工厂接口.它仍然没有涵盖所有情况,但它比将15个参数传递给构造函数更好.顺便说一句.你确定那些控制器不应拆分吗?
注意:这不是服务提供商反模式.
在使用多个存储库之后,我们需要调用SaveChanges方法,但是我们应该调用哪个存储库?
向工作单元模式问好.工作单元是您的应用程序中的逻辑事务.它将逻辑事务中的所有更改保持在一起.存储库不应对持续更改负责 - 工作单元应该是.有人提到那DbContext是Repository模式的实现.事实并非如此.它是工作单元模式DbSet的实现,是Repository模式的实现.
你需要的是持有上下文实例的中心类.上下文也将传递给存储库,因为它们需要它来检索数据,但只有中心类(工作单元)才会提供保存更改.如果您需要更改隔离级别,它还可以处理数据库事务.
处理单位应该在哪里?这取决于您的逻辑操作是在哪里编排的.如果操作是直接在控制器的操作中编排的,则还需要在操作中包含工作单元,并SaveChanges在完成所有修改后调用.
如果您不关心过多的关注点,您甚至可以将工作单元和工厂组合成单一类.这将我们带到你的身边DataBucket.
我认为在这种情况下使用工作单元模式是绝对正确的。这不仅可以防止您SaveChanges在每个存储库上都需要一个方法,而且还为您提供了一种从代码内部而不是数据库本身处理事务的好方法。我Rollback在我的 UOW 中包含了一个方法,这样如果出现异常,我可以撤消该操作已经对我的DataContext.
为了防止奇怪的依赖问题,您可以做的一件事是将相关存储库分组到它们自己的工作单元上,而不是使用一个大 DataBucket 来保存您拥有的每个存储库(如果这是您的意图)。每个 UOW 只需要在与其包含的存储库相同的级别上进行访问,其他存储库可能不应该依赖于其他 UOW 本身(您的存储库不需要使用其他存储库)。
如果想要成为该模式的更纯粹主义者,您还可以构建 UOW 来表示单个工作单元。您定义它们来表示域中的特定操作,并为其提供完成该操作所需的存储库。如果域中多个操作需要使用单个存储库,那么单个存储库可以存在于多个 UOW 上。
例如,aPlaceCustomerOrderUnitOfWork可能需要 a CustomerRepository、OrderRepository、BillingRepository和 aShippingRepository
AnCreateCustomerUnitOfWork可能只需要一个CustomerRepository. 无论哪种方式,您都可以轻松地将这种依赖关系传递给其使用者,UOW 的更细粒度的接口可以帮助您确定测试目标并减少创建模拟的工作量。