如何在域模型实体类中注入帮助程序依赖项 [英] How to Inject Helper Dependencies in Domain Model Entity Classes

查看:131
本文介绍了如何在域模型实体类中注入帮助程序依赖项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我正在努力将我的桌面/ WPF解决方案从使用服务定位器模式转换为使用依赖注入。到目前为止,它相对来说比较轻松(因为在两种情况下都使用相同的UnityContainer):我只是删除对全局/静态ServiceLocator的每个调用,并将依赖项放入构造函数中。但是当涉及到我的一个实体类中存在的帮助程序服务时,我感到很沮丧。

So I'm well underway in my effort to convert my desktop/WPF solution from using Service Locator pattern to using Dependency Injection. So far it has been relatively painless (since the same UnityContainer is used in both cases): I just remove each call to my global/static ServiceLocator and put the dependency in the constructor. But I'm stumped when it comes to a helper service that exists in one of my entity classes.

当前,我有这样的东西:
一个单例帮助程序服务,其中不包含任何状态,而仅包含一些常用逻辑:

Currently I have something like this: A singleton helper service which doesn't contain any state but just some commonly-used logic:

interface ICalculationsHelper { double DoCompletelyCrazyCalculations(); }

然后在域模型实体中使用:

then, its use in an domain model entity:

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();
    }
}

...对于实体框架来说这没问题物化对象,并且我可以通过多种方式使用该类(例如,包装在ViewModels中,在列表中进行操作等)非常简单,因为我只是在处理默认构造函数。

...which is no problem for Entity Framework to materialize objects, and I can use this class in many ways (e.g. wrapping in ViewModels, manipulating in lists, etc) very simply because I'm just dealing with a default constructor.

如果我这样做,现在会发生什么(删除ServiceLocator并将依赖帮助器放入构造函数中):

Now what happens if I this (remove ServiceLocator and put the dependent helper in the constructor):

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传递给实体。这是主要的混乱情况,并使每种算法复杂化。将此次要服务以静默方式加载到后台(按照服务定位器模式)似乎更加干净。
3)如果事实证明在这种情况下使用服务定位器更好,应该如何对该域类进行单元测试(模拟服务)?

1) How is EF supposed to inject a new one of these helpers for each entity? 2) Say my app manipulates the entity in 100 places and in various different ways--all with a default constructor. Now all the sudden I have to modify each of those algorithms to manually pass an ICalculationsHelper into the entity. This is major clutter and complicates each algorithm. It seems much cleaner to have this "minor" service silently loaded in the background (as per service locator pattern). 3) If it turns out that using service locator is better in this case, how is this domain class supposed to be unit tested (mocking the service)?

谢谢

推荐答案


1)EF应该如何为每个帮助者注入一个新的帮助者

1) How is EF supposed to inject a new one of these helpers for each entity?

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

It's not possible to use the IoC container for this. An IoC container knows how to inject dependencies into (root) objects it creates itself, not objects created otherwise. So if you want entities to have this dependency (which is disputable, see later), you can subscribe a handler to the wrapped ObjectContext's ObjectMaterialized event:

((IObjectContextAdapter)this).ObjectContext.ObjectMaterialized += 
     ObjectContext_ObjectMaterialized;

在处理程序中,您可以检查 CrazyNumber 已实现,并为其分配了 ICalculationsHelper 。因此,这不是构造函数注入,而是属性注入(不是IoC容器的注入)。在下面的注释之前,您也可以在不注入服务的情况下在其中生成其 Thenumber

In the handler you can check whether a CrazyNumber was materialized and assign a ICalculationsHelper to it. So this is not constructor injection, but property injection (sort of, not by an IoC container). Preluding on the remarks below, you could also generate its Thenumber there without ever injecting the service.


2)...使用这种(...)服务定位器模式似乎更清洁

2) ...It seems much cleaner to have this (...) service locator pattern

我同意。如果IoC出于某种原因无法工作,则SL排名第二。

I agree. If IoC can't work for whatever reason, SL is second best.


3)应该如何对该域类进行单元测试?

3) how is this domain class supposed to be unit tested?

这是一般的IoC问题。对于单元测试,如果IoC容器本身具有在单元测试上下文中无法实现的依赖关系,则您的IoC容器应该能够注入模拟服务

That's a general IoC issue. For unit tests your IoC container should be able to inject a mock service if the service itself has dependencies that can't be fulfilled in the context of unit tests

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

But I doubt whether you should inject this service into the entities at all. This touches a broad subject, but I'll give a thought or two here:


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

  • I especially don't like the fact that the service does its work in the constructor. The object is constructed by EF, so whatever the service does, it may interfere with object construction by EF.

为什么应该 CrazyNumber 本身是否创建其 TheNumber 属性(允许您的代码只是真实情况的替代者)?从面向对象的角度来看,如果它结合了数据和行为,就应该发生这种情况。换句话说,如果 CrazyNumber 包含生成数字所需的状态。但是,这种状态永远无法保证在构造函数中是完整或稳定的。 (在 ObjectMaterialized 之后)。而且,如果不需要这种状态,那么该行为就根本不应该存在(单一职责)。

Why should CrazyNumber create its TheNumber property itself (granted that your code is just a stand-in for the real case)? From an object-oriented point of view this should happen if it combines data and behavior. In other words, if CrazyNumber contains the state that's required to generate the number. But this state can't ever be guaranteed to be complete or stable in a constructor. (It is after ObjectMaterialized). And if this state is not required, then the behavior shouldn't be there at all (single responsibility).

也许您的示例有些人为,需要服务来稍后创建号码,例如首次访问时。然后, CrazyNumber 也不应具有此依赖关系,因为可能存在从不生成该数字的代码路径。在这种情况下,该服务是一个松散的依赖项,很难确定何时需要它。最好在确实需要时通过方法注入注入它:

Maybe your example is a bit contrived, maybe the service is needed to create the number later, e.g. when it's first accessed. Then too, CrazyNumber shouldn't have this dependency, because there are likely to be code paths in which the number is never generated. In that case, the service is a loose dependency and it will be hard to tell when it actually needs it. It would be better to "inject" it by method injection when it's really needed:

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


这篇关于如何在域模型实体类中注入帮助程序依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆