.net 如何在域模型实体类中注入辅助依赖项

.net 如何在域模型实体类中注入辅助依赖项,.net,entity-framework,dependency-injection,service-locator,domain-model,.net,Entity Framework,Dependency Injection,Service Locator,Domain Model,因此,我正在努力将我的桌面/WPF解决方案从使用服务定位器模式转换为使用依赖注入。到目前为止,它相对来说比较轻松(因为在这两种情况下都使用相同的UnityContainer):我只需删除对全局/静态ServiceLocator的每个调用,并将依赖项放入构造函数中。但当涉及到存在于我的一个实体类中的助手服务时,我感到很困惑 目前我有这样的想法: 单例帮助器服务,不包含任何状态,只包含一些常用逻辑: interface ICalculationsHelper { double DoCompletel

因此,我正在努力将我的桌面/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();
    }
}
1) EF如何为每个实体注入一个新的助手? 2) 假设我的应用程序在100个位置以各种不同的方式操纵实体——所有操作都使用默认构造函数。现在,我突然不得不修改这些算法中的每一个,以手动将iCalculationHelper传递到实体中。这是主要的混乱,使每种算法复杂化。在后台无声地加载这个“次要”服务似乎要干净得多(根据服务定位器模式)。 3) 如果在这种情况下使用服务定位器更好,那么这个域类应该如何进行单元测试(模拟服务)

谢谢

1) EF如何为每个实体注入一个新的助手

不可能为此使用IoC容器。IoC容器知道如何将依赖项注入到它自己创建的(根)对象中,而不是以其他方式创建的对象中。因此,如果您希望实体具有此依赖关系(这是有争议的,请参阅下文),您可以订阅包装的
ObjectContext
事件的处理程序:

在处理程序中,您可以检查
CrazyNumber
是否已具体化,并为其分配
iCalculationHelper
。所以这不是构造函数注入,而是属性注入(某种程度上,不是通过IoC容器)。根据下面的备注,您还可以在那里生成其
编号
,而无需注入服务

2) …使用此(…)服务定位器模式似乎更干净

我同意。如果国际奥委会因任何原因不能工作,SL是第二好的

3) 这个域类应该如何进行单元测试

这是国际奥委会的一个普遍问题。对于单元测试,如果服务本身具有无法在单元测试上下文中实现的依赖项,那么IoC容器应该能够注入模拟服务


但我怀疑您是否应该将此服务注入实体中。这涉及到一个广泛的主题,但我在这里想一想:

  • 我尤其不喜欢服务在构造函数中完成其工作这一事实。对象是由EF构造的,因此无论服务做什么,它都可能干扰EF的对象构造

  • 为什么要
    CrazyNumber
    创建其
    Number
    属性本身(假设您的代码只是实际案例的替身)?从面向对象的角度来看,如果它将数据和行为结合在一起,这应该会发生。换句话说,if
    CrazyNumber
    包含生成数字所需的状态。但在构造函数中,这种状态永远不能保证是完整的或稳定的。(它在对象物化之后)。如果不需要这种状态,那么行为就不应该存在(单一责任)

  • 可能您的示例有点做作,可能以后需要该服务来创建号码,例如首次访问时。同样,
    CrazyNumber
    也不应该有这种依赖关系,因为可能存在从未生成数字的代码路径。在这种情况下,服务是一个松散的依赖项,很难判断它何时真正需要它。在真正需要时,最好通过方法注入来“注入”:

    public void CreateCrazyNumber(ICalculationsHelper helper)
    {
        TheNumber = helper.DoCompletelyCrazyCalculations();
    }
    

相关:感谢您的详尽回答。在我设计的示例中,helper应该表示类似Math.Sqrt()的内容,但它是特定于应用程序的,并且是一些类所必需的。我不想在每个需要的类中重复同样的数字运算算法。我最初将代码分解成一个静态助手类,然后我在某个地方读到,在现代OO应用程序中,静态类不适合这个用途,所以我将其替换为一个位于singleton的服务。这让我想到了现在,尝试注入服务。不知怎么的,我错过要点了吗?谢天谢地,你的课不好吗?取决于。。。我认为这对于静态实用程序类来说是典型的,就像
Math
本身一样。静态类+状态,这很糟糕。不是没有副作用的静态函数。谢谢——知道仅用于逻辑的静态类是可以的,这让我松了一口气。要跳过的篮圈太少了。
((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += 
     ObjectContext_ObjectMaterialized;
public void CreateCrazyNumber(ICalculationsHelper helper)
{
    TheNumber = helper.DoCompletelyCrazyCalculations();
}