【问题标题】:Why would one use a third-party DI Container over the built-in ASP.NET Core DI Container?为什么要使用第三方 DI 容器而不是内置的 ASP.NET Core DI 容器?
【发布时间】:2015-08-21 06:52:21
【问题描述】:

目前缺乏关于 DI 主题的文档 - Dependency Injection。与现有解决方案(如(Ninject、Autofac、StructureMap))相比,使用内置 DI 的优点/缺点是什么?目前默认依赖注入的限制是什么(如果有的话)?

【问题讨论】:

  • 目前是否有任何标准 DI 容器(ninject 等)的基于 dotnetcore 的实现?
  • @AbhijeetPatel 现在,大多数(如果不是全部)DI 容器与 .NET Core 和 .NET Standard 兼容。

标签: asp.net dependency-injection asp.net-core


【解决方案1】:

对于任何实施松散耦合并遵循 SOLID 原则的合理规模的应用程序的产品开发,.NET Core 的 DI 容器 (MS.DI) 都不适合,因为:

  • 它不能帮助您验证配置,因此很难诊断出常见错误配置引起的问题。在一个合理大小的应用程序中,实际上很难自己发现这些错误。 更新:MS.DI 的第 3 版现在包含一个名为 ValidateOnBuild 的功能,但它唯一要做的就是检查是否可以满足所有注册的所有依赖项。
  • 不可能使用拦截器应用Cross-Cutting Concerns 或以可维护的方式应用decorators。这使得维护任何合理大小的应用程序变得更加昂贵。 更新:有几个第三方库试图填补这一空白,但由于 MS.DI 的限制,它们无法完全填补这一空白(例如,装饰开放通用注册,确保处理装饰实例等)
  • 虽然 MS.DI 支持开放泛型抽象到开放泛型实现的映射,但它的实现相当 naive 并且无法使用具有类型约束、更复杂的泛型类型映射和方差的泛型类型。李>
  • impossible 进行条件/上下文注册,这样注册只会被注入到特定的消费者集合中,同时使用自动连接。例如当有两个组件Service1Service2 都依赖于ILogger 时,您可能希望将Service1NullLoggerService2FileLogger 一起注入,或者您希望Service1Logger<Service1>Service2Logger<Service2>

存在这些限制的主要原因是,内置容器的目标是为特别是框架本身提供 DI 功能,同时将其功能集保持在最低限度,希望更成熟的 DI 容器能够能够与之融合。换句话说,它尝试充当最小公分母 (LCD)。由于它的 LCD 功能,它永远无法成长为一个成熟的 DI Container,可用于应用程序开发(并非不违背作为 LCD 的承诺)。

如果您从一个新的简单项目开始,我的建议是申请Pure DI。这意味着您在Composition Root 内手动连接组件 使用容器并且无需 创建您自己的 DI 容器。相反,您可以通过插入 your custom IControllerActivator 来解析您的类型。稍后,当Auto-Wiring、Auto-Registration 和 Interception 等功能可以提高您的 Composition Root 的可维护性时,请切换到符合您要求的已建立的 DI 库之一。

【讨论】:

  • 对 ASP.NET Core 2.0 仍然有效?
  • @Legends。是的。这个答案仍然适用于 .NET Core 2.0。
  • @Steven : .net core 2.2 和 3-preview 有什么改进吗?
  • @leox 不!而且您不必期望在这方面取得任何进展。微软用他们的容器把自己画在一个角落里。他们的 DI Container 永远不会演变成功能丰富的 DI Container。
  • 是的。 .NET 5.0 仍然是这种情况。
【解决方案2】:

这里解释一下:

  • 瞬态 - 每次都会创建一个新实例
  • Scoped - 在当前范围内创建单个实例。相当于当前作用域中的 Singleton
  • 单例 - 创建单个实例,它的行为类似于单例
  • 实例 - 始终提供特定实例。您负责其初始创建

Alpha 版本有这个限制:

  • 只支持构造函数注入
  • 它只能用一个且只有一个公共构造函数来解析类型
  • 它不支持高级功能(如每个线程范围或自动发现)

如果您不编写非常复杂的产品,默认 DI 容器应该足够了。在其他情况下,您可以尝试您已经提到的具有高级功能的库。

我的建议是从默认的开始并更改实现 什么时候(如果)你碰到了你不能用它做的东西。

【讨论】:

  • Alpha 版本不再相关。
【解决方案3】:

另外,有人可以帮我了解这些注册之间的区别吗?

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IService, Service>();
    services.AddScoped<IService, Service>();
    services.AddSingleton<IService, Service>();
    services.AddInstance(service);
}

https://stackoverflow.com/revisions/30681477/8

  • 瞬态 - 每次检索时都会实例化
  • 作用域 - 每个 http 请求实例化一次,并且在 http 请求的整个生命周期内都可用
  • 单例 - 实例化一次,在您的应用程序的整个生命周期内都可用
  • 实例 - 相当于单例,除了您提供对象实例而不是创建实例的框架

来源:http://www.khalidabuhakmeh.com/asp-vnext-dependency-injection-lifecycleshttp://dotnetliberty.com/index.php/2015/10/15/asp-net-5-mvc6-dependency-injection-in-6-steps/

为了解释为什么它被称为作用域更多,请考虑以下代码:

// app startup
var services = new ServiceCollection();
services.AddScoped<IService, Service>();
var startupServices = services.BuildServiceProvider();
// request init
var requestServices = statupServices.CreateScope().ServiceProvider;

因此,在 ASP.NET 中,HttpContext.RequestServices 的创建方式与上面在请求开始时调用 CreateScope 的方式类似。但它可以在 ASP.NET 之外的上下文中使用,在这种情况下,范围的含义可能与每个请求的含义不同。

【讨论】:

    【解决方案4】:

    回答您的第一个问题:ASP.NET docs 似乎已更新,现在明确说明每种注册类型的用途:

    ASP.NET 服务可以配置为以下生命周期:

    瞬态

    每次请求时都会创建瞬态生命周期服务。 此生命周期最适合轻量级、无状态服务。

    作用域

    范围内的生命周期服务为每个请求创建一次。

    单例

    单例生命周期服务在首次创建时创建 请求,然后每个后续请求都将使用相同的 实例。如果您的应用程序需要单例行为,则允许 推荐使用服务容器来管理服务的生命周期 而不是实现单例设计模式并管理您的 对象在类中的生命周期。

    实例 [仅限 RTM 前!]

    您可以选择将实例直接添加到服务容器。 如果这样做,此实例将用于所有后续请求 (这种技术将创建一个单例范围的实例)。一把钥匙 Instance services 和 Singleton services 的区别在于 Instance 服务是在 ConfigureServices 中创建的,而 单例服务在第一次被请求时被延迟加载。


    在 RTM 中更新

    请注意,在 Asp.Net Core RTM docs 中删除了实例。 Instance 和 Singleton 基本上是一样的,但是它们有不同的初始化语义(Singleton 是延迟加载的)。但是现在没有 AddInstance API,只有 AddSignleton 可以接受已经创建的实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-18
      • 2020-09-20
      • 1970-01-01
      • 2017-09-21
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多