当我有多个Unit-Of-Work实现时,我应该如何抽象我的业务级别类?

时间:2014-03-04 18:25:02

标签: dependency-injection

我正在尝试创建一个名为 Products 的业务逻辑类,我在其中传入一个工作单元(这个工作单元是通过依赖注入创建的):

var products = new Products(uow); //uow implements IUnitOfWork

产品有一种名为添加

的方法
int newID = products.Add("my widget");

现在假设我有两个IUnitOfWork实现, SqlServer InMemoryMock Products 类的所有方法的业务逻辑基本相同,但在某些情况下,内部工作基于持久性而更改,或者在这种情况下使用IUnitWork的哪个实现。我们假设添加方法就是其中一种情况。

为了实现不同的实现,我可以将Products设为抽象类,然后从中继承并使添加方法抽象并强制实现覆盖此方法。

然而,这需要我创建某种ProductFactory,它将返回正确的类:

var products = ProdcutFactory.Get(uow); //return the SqlServer or InMemoryMock version

这将要求我的应用程序引用具有ProductFactory的程序集,而ProductFactory又引用了SqlServer和InMemoryMock程序集。根据马克·西曼的不可思议的书,这是一个禁忌。

有没有更好的方法来完成抽象尝试? (有没有办法解决马克·西曼的问题?我知道他对S.O.活跃。)

编辑:

对于我的SqlServer实现,我需要实际保存(不提交)我添加的行以获取新行的ID以将其返回给使用者(MVC控制器)。所以我的产品的SqlServer实现将有这个额外的调用。我的MVC控制器不应该关心它使用哪种类型的产品类。

我正在尝试创建一种创建 Products 类的方法,该类与传入控制器的IUnitOfWork类型相匹配。 我担心的是我的MVC项目不应该引用我的InMemoryMock实现

是否在Mark的书中引用了AbstractFactory(页面?)我开始觉得DI容器会在这个工厂中传递。

编辑#2:编辑#2: 假设我的添加方法必须使用存储库向我的Sql数据库插入一行,并且关联表的ID列是一个自动递增列。然后,我需要该ID用于后续操作。我能获得该值的唯一方法是在IUnitOfWork上调用Save方法(好吧,我可以调用Commit,但我还不想提交)

uow.ProductRepo.Insert(new UserModel(){.....
uow.Save();
var uid = uow.ProductRepo.Get(u => u.someuniquecode == someUniqueCode)....... (etc)

现在,如果我在做Mock版本,我不需要在UOW上调用Save方法,但我宁愿在插入过程中提供ID。

var uid = uow.ProductRepo.Get(some lambda to get the largest ID) + 1
uow.ProductRepo.Insert(new UserModel(ID = uid, .......

所以我基本上有两个版本的添加,每个版本都做类似的事情,但是我的消费者对此毫无兴趣。


我正在通过依赖注入创建我的UOW(包含repos)。我创建了一些“职员”课程,它们采用UOW并执行业务功能。无论我的UOW类型(实际上是基于存储库类型),业务逻辑都是相同的,但是存储库的工作方式略有不同,特别是自动增量索引功能。

我所做的是创建一个名为 Products 的抽象业务类,它实现了IProductClerk(它本身实现了IDisposable)并将业务逻辑放在那里。我有两个独立的库(business.mock和business.sqlserver),我实现这个抽象类,覆盖任何不能使用“标准”逻辑的方法。我的消费者获得了IProductClerk的实例,因此两个实现看起来都一样(希望这满足LSP)。

我还有一个IProductClerkFactory接口,在我的business.mock和business.sqlserver类中,我创建了 ProductClerkFactory:IProductClerkFactory ,它返回了它们的IProductClerk实现。

在需要IProductClerk的控制器中,我将 IProductClerkFactory productClerkFactory 添加到构造函数中,然后在我的组合根中配置IProductClerkFactory应该使用business.sqlserver.clerk.ProuctClerkFactory。

当我的控制器被调用时,我得到了正确类型的IProductClerkFactory(我的消费者并不关心),从那里我可以生成一个IProductClerk并传入持久性依赖(工作单元)。

对于我的单元测试,我从模拟库创建了一个工厂实例,对于集成测试,我使用了sql库。

这看起来如何?

1 个答案:

答案 0 :(得分:1)

如果我理解正确,那么当uow基于SQL Server时,您希望客户端代码执行此操作:

uow.ProductRepo.Insert(new UserModel(){.....
uow.Save();
var uid = uow.ProductRepo.Get(u => u.someuniquecode == someUniqueCode)....... (etc)

但是当它基于测试双重时:

var uid = uow.ProductRepo.Get(some lambda to get the largest ID) + 1
uow.ProductRepo.Insert(new UserModel(ID = uid, .......

但是,这不是多态行为。它违反了Open Closed Principle,因为它要求客户了解可用的实现,并且它可能也违反了Liskov Substitution Principle,尽管我对此不太确定。

为什么不使用生产交互,然后为两个库正确实现IUnitOfWork

如果你真的必须有这种不同的行为,那么你应该隐藏Strategy背后的这些差异,这可能再次作为Facade Service而不是IUnitOfWork 。然后将Facade Service注入Products而不是IUnitOfWork