【问题标题】:How do I validate the DI container in ASP.NET Core?如何在 ASP.NET Core 中验证 DI 容器?
【发布时间】:2018-03-07 10:03:19
【问题描述】:

在我的Startup 类中,我使用ConfigureServices(IServiceCollection services) 方法设置我的服务容器,使用来自Microsoft.Extensions.DependencyInjection 的内置DI 容器。

我想在单元测试中验证依赖关系图,以检查是否可以构建所有服务,这样我就可以修复单元测试期间丢失的任何服务,而不是让应用程序在运行时崩溃。在之前的项目中,我使用了 Simple Injector,它有一个用于容器的 .Verify() 方法。但是我找不到任何与 ASP.NET Core 类似的东西。

是否有任何内置的(或至少推荐的)方法来验证整个依赖图是否可以构建?

(我能想到的最愚蠢的方法是这样的,但由于框架本身注入的开放泛型,它仍然会失败):

startup.ConfigureServices(serviceCollection);
var provider = serviceCollection.BuildServiceProvider();
foreach (var serviceDescriptor in serviceCollection)
{
    provider.GetService(serviceDescriptor.ServiceType);
}

【问题讨论】:

  • 我怀疑答案是,如果您需要这样的功能来使用具有它的第 3 方 DI 容器。或者,您可以查看具有它的 DI 容器的源代码,并构建您自己的 Microsoft.DependencyInjection 扩展来提供它。
  • “在以前的项目中我使用过简单的注射器”。为什么不再使用 Simple Injector?它似乎优雅地解决了您的问题。
  • @Steven 真的!由于这是一个新项目,我正在尝试尽可能多地使用内置功能,但如果事实证明在框架中没有进行此验证的好方法,则使用 Simple Injector 可能是解决方案。
  • 您找到了一个好的解决方案吗?我刚刚发布了同样的问题
  • @mcintyre321 终于在 aspnetcore 3 中得到了它stackoverflow.com/a/60374778/5112433

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


【解决方案1】:

在 ASP.NET Core 3 中添加了内置 DI 容器验证,默认情况下仅在 Development 环境中启用。如果缺少某些内容,容器会在启动时引发致命异常。

请记住,默认情况下,控制器不会在 DI 容器中创建,因此在控制器被注册到 DI 之前,典型的 Web 应用不会从该检查中获得太多信息:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddControllersAsServices();
}

要禁用/自定义验证,请添加 IHostBuilder.UseDefaultServiceProvider 调用:

public class Program
{
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            //...
            .UseDefaultServiceProvider((context, options) =>
            {
                options.ValidateOnBuild = false;
            });

此验证功能有几个限制,请在此处阅读更多信息:https://andrewlock.net/new-in-asp-net-core-3-service-provider-validation/

【讨论】:

  • AddControllersAsServices 是这里的关键。没有它,控制器不会被注册,因此不会被验证。
【解决方案2】:

实际上,我只是使用了您问题中的示例并进行了一些修改,它对我来说效果很好。在我的命名空间中按类过滤背后的理论是,这些最终会询问我关心的所有其他内容。

我的测试看起来很像这样:

[Test or Fact or Whatever]
public void AllDependenciesPresentAndAccountedFor()
{
    // Arrange
    var startup = new Startup();

    // Act
    startup.ConfigureServices(serviceCollection);

    // Assert
    var exceptions = new List<InvalidOperationException>();
    var provider = serviceCollection.BuildServiceProvider();
    foreach (var serviceDescriptor in services)
    {
        var serviceType = serviceDescriptor.ServiceType;
        if (serviceType.Namespace.StartsWith("my.namespace.here"))
        {
            try
            {
                provider.GetService(serviceType);
            }
            catch (InvalidOperationException e)
            {
                exceptions.Add(e);
            }
        }
    }

    if (exceptions.Any())
    {
        throw new AggregateException("Some services are missing", exceptions);
    }
}

【讨论】:

  • 您能否更新您的答案并添加您迭代的serviceCollectionservices 来自哪里
  • 如果任何服务都实现了 IDisposable,那么处置提供者不是一个好主意吗?
  • 我还将创建一个服务范围来创建范围服务。
【解决方案3】:

我在我的一个项目中遇到了同样的问题。我的决心:

  1. 添加 AddScopedService、AddTransientService 和 AddSingletonService 等方法,将服务添加到 DI,然后将其添加到某个列表。使用此方法代替 AddScoped、AddSingleton 和 AddTransient

  2. 在启动应用程序第一次时,我遍历此列表并调用 GetRequiredService。如果任何服务无法解析,应用程序将无法启动

  3. 我有 CI:在提交时自动构建和部署以开发分支。因此,如果有人合并了破坏 DI 的更改,应用程序就会失败,我们都知道这一点。

  4. 如果您想更快地做到这一点,您可以将 Dmitry Pavlov 的答案中的 TestServer 与我的解决方案一起使用

【讨论】:

    【解决方案4】:

    这是一个可以添加到项目中的单元测试:

    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    using [X/N]Unit;
    
    namespace MyProject.UnitTests
    {
        public class DITests
        {
            [Fact or Test]
            public void AllServicesShouldConstructSuccessfully()
            {
                Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder
                            .UseDefaultServiceProvider((context, options) =>
                            {
                                options.VailidateOnBuild = true;
                            })
                            .UseStartup<Startup>();
                    }).Build();
            })
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-21
      相关资源
      最近更新 更多