将您的应用程序容器包含在服务的构造函数中是否被认为是糟糕的DI实践?

时间:2013-07-19 17:30:21

标签: c# dependency-injection simple-injector

首先,我使用Simple Injector,但我认为这个问题适用于任何DI框架。

在创建要为应用程序注册的服务时,将服务容器通过其构造函数传递给服务是否被视为不良做法?

例如。请考虑以下代码。

//IServiceInterface.cs
interface IServiceInterface {}

//MyService.cs
//All standard using statements here...
using SimpleInjector;

class MyService : IServiceInterface
{
    private _container {get; set;}

    public MyService(Container container) 
    {
        _container = container;

        //Construct!
    }
}

//MyApp.cs

public Contrainer container;
....

//My application bootstrapper method
void Bootstrap()
{
    var container = new Container();
    container.RegisterSingle<IServiceInterface >(() => new MyService(container));
    container.Verify();
    this.container = container;
}

从上面的方法可以看出,我定义了一个采用Simple Injector容器的服务类。当我注册容器时,我将与我的应用程序关联的容器传递给服务。

当您定义一个服务时,这似乎是一种有用的方法,该服务将位于一个单独的项目中,甚至可能位于不同的名称空间中,需要在应用程序生命周期的某个时刻注册新服务。但是,我没有在任何例子中看到过这种情况,在我尝试这样的事情之前,我想确保这种方法是正确的。

这种行为被认为是良好的DI练习吗?如果没有,您如何获得应用程序DI容器,并在服务中注册需要的新服务?

更新 - 其他详细信息

由于两个原因,我决定开始使用依赖注入来创建一个新项目。一,这是一个庞大的项目,可能包括10-12个Visual Studio项目,其次,许多这些项目包含我多年来从一个应用程序复制并粘贴到另一个应用程序的代码,然后稍微修改了它们需要的。足够的,现在是时候编写我自己的业务逻辑框架,与我们公司合作,正如我们所需要的那样。

这个第一个大项目似乎是从DI和我的自制框架开始的地方。为了一次构建和测试这个应用程序,我定义了很多接口和“shell”服务类。这样,我就可以连接我的顶级应用程序,并在项目完成后更新依赖项并链接到我的解决方案中。

由于这是一个如此大的应用程序,我有服务,需要依赖服务......这将进一步依赖于服务。

我的想法是我的应用程序应该只注册验证用户身份和加载视图所需的服务。 View服务应该注册Model视图。 ModelView服务应该注册它们相关的Model服务,它将注册数据库连接服务......叹息,它将EVENTUALLY注册服务器端同步服务,该服务将注册本地数据库连接服务和Web应用程序服务。唷!声音混乱?嗯,有点儿。

我的想法是我可以定义这些可以接受Container对象的类,然后每个服务都将使用容器来获取可能已经存在的任何底层服务,或者如果尚未创建一个新实例,则实例化它。

对于instane,我的用户Auth服务可以通过应该与我的Model服务共享的ILocalDB服务来缓存信息。如果我在启动应用程序时注册所有这些服务,那么应用程序将会缓慢,整个注册看起来会非常粗糙。

我认为必须有一个优雅的解决方案。我错过了什么?

2 个答案:

答案 0 :(得分:3)

在我看来,传递容器并不是好设计。您实际上是在传递一个通用对象构造工厂,因此绕过了明确关于依赖关系的固有价值。在实践中,您也可以在Service类上声明container的静态属性。

如果我需要动态构造对象,我通常会传入工厂而不是显式实例。比如,传入ILogFactory而不仅仅是ILog的一个实例。这使得从代码中可以看出动态构造的是什么,代价是一些工厂和构造函数参数。

另一种选择,如果你不需要创建多个依赖项实例,但是你知道你只需要其中的一些,那就是确保对象构造很轻。然后,明确声明所有依赖项并不重要,因为只有在实际使用它们时它们才会发生成本。

答案 1 :(得分:2)

标题中基本问题的答案已经由@Steven回答了 - 是的,将容器注入任何类通常被认为是不好的做法。 @Steven是SimpleInjector的作者,坚持这个原则 - see here

我不清楚你还在问什么,但这里有一些可能有用的信息。

  • SimpleInjector通常不建议在整个应用程序生命周期内注册服务,并且最好所有注册都在组合根中启动时完成。 See here
  

从容器中解析第一个类型时,将锁定容器以进行进一步更改。在此之后调用其中一个注册方法时,容器将抛出异常。容器无法解锁。此行为是固定的,无法更改。如果必须在此点之后注册新类型,则可以使用未注册的类型解析。

  • 如果有对象需要一些时间来实例化,那么可以注入Lazy<>个实例,这样实例就不会被实例化,直到/除非在代码的调用路径中显式引用它。 See here

  • 对象生命周期管理应该处理传入现有实例/实例化新实例的所有复杂性。 See here

  • 为了防止注册过程变得“粗糙”,您可以将流程划分为每个注册解决方案区域的类,例如DAL,CommandHandlers,Services等。这些类应该在组合根内部,负责引导整个应用程序,整个引导过程只在启动时调用一次。组合根甚至不需要显式引用包含解决方案中所有实现的所有程序集;它只需要引用您定义的所有服务。 See here and here