因此,我正在努力将我的桌面/ WPF解决方案从使用服务定位器模式转换为使用依赖注入。到目前为止它一直相对无痛(因为在两种情况下使用相同的UnityContainer):我只是删除对我的全局/静态ServiceLocator的每次调用,并将依赖项放在构造函数中。但是当涉及到我的一个实体类中存在的辅助服务时,我感到很难过。
目前我有这样的事情: 单例帮助程序服务,它不包含任何状态,只包含一些常用逻辑:
interface ICalculationsHelper { double DoCompletelyCrazyCalculations(); }
然后,它在域模型实体中的使用:
class CrazyNumber
{
public int Id { get; set; }
public string Name { get; set; }
public double TheNumber { get; set; }
ICalculationsHelper _helper = ServiceLocator.Resolve<ICalculationsHelper>();
public CrazyNumber()
{
CreateCrazyNumber();
}
private void CreateCrazyNumber()
{
TheNumber = _helper.DoCompletelyCrazyCalculations();
}
}
...实体框架实现对象没有问题,我可以在很多方面使用这个类(例如包装在ViewModel中,在列表中操作等),因为我只是处理一个默认构造函数。
现在如果我这样做会发生什么(删除ServiceLocator并将依赖帮助器放在构造函数中):
class CrazyNumber
{
public int Id { get; set; }
public string Name { get; set; }
public double TheNumber { get; set; }
ICalculationsHelper _helper;
public CrazyNumber(ICalculationsHelper helper)
{
_helper = helper;
CreateCrazyNumber();
}
private void CreateCrazyNumber()
{
TheNumber = _helper.DoCompletelyCrazyCalculations();
}
}
1)EF如何为每个实体注入一个新的帮助者? 2)假设我的应用程序在100个地方以各种不同的方式操纵实体 - 所有这些都使用默认构造函数。现在突然间我必须修改每个算法以手动将ICalculationsHelper传递给实体。这是主要的混乱,并使每个算法复杂化。这个&#34; minor&#34;似乎更清洁。服务在后台静默加载(根据服务定位器模式)。 3)如果事实证明在这种情况下使用服务定位器更好,那么该域类应该如何进行单元测试(模拟服务)?
由于
答案 0 :(得分:2)
1)EF如何为每个实体注入一个新的帮助者?
无法使用IoC容器。 IoC容器知道如何将依赖项注入它自己创建的(根)对象,而不是其他方法创建的对象。因此,如果您希望实体具有此依赖关系(这是有争议的,请参阅后面的内容),您可以为包装的ObjectContext
ObjectMaterialized
事件订阅处理程序:
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized +=
ObjectContext_ObjectMaterialized;
在处理程序中,您可以检查CrazyNumber
是否已实现并为其分配ICalculationsHelper
。所以这不是构造函数注入,而是属性注入(排序,而不是IoC容器)。根据下面的评论,你也可以在没有注入服务的情况下在那里生成Thenumber
。
2)......拥有这个(...)服务定位器模式似乎更清晰
我同意。如果IoC因任何原因无法工作,那么SL是第二好的。
3)这个域类应该如何进行单元测试?
这是IoC的一般问题。对于单元测试,如果服务本身具有在单元测试环境中无法实现的依赖性,则IoC容器应该能够注入模拟服务
但我怀疑你是否应该将这项服务注入实体。这涉及广泛的主题,但我会在这里给出一两个想法:
我特别喜欢这个服务在构造函数中完成它的工作。该对象由EF构造,因此无论服务如何,都可能干扰EF的对象构造。
为什么CrazyNumber
应该创建自己的TheNumber
属性(授予你的代码只是真实案例的替身)?从面向对象的角度来看,如果它结合了数据和行为,就会发生这种情况。换句话说,如果CrazyNumber
包含生成该数字所需的状态。但是这种状态在构造函数中无法保证完整或稳定。 (ObjectMaterialized
之后 。如果不需要这种状态,那么行为就不应该存在(单一责任)。
也许您的示例有点做作,可能需要服务才能在以后创建数字,例如何时首次访问。那么,CrazyNumber
也不应该具有这种依赖性,因为可能存在从不生成该数字的代码路径。在这种情况下,服务是一个松散的依赖,很难说它何时需要它。最好注射&#34;注射&#34;当它确实需要时通过方法注入:
public void CreateCrazyNumber(ICalculationsHelper helper)
{
TheNumber = helper.DoCompletelyCrazyCalculations();
}