ASP.NET Core DI如何验证所有注册类型?

6

我想要检查在 Startup.cs 中建立的类型注册在运行时是否都是有效的(可以在启动服务或测试套件的一部分中进行)。 Lamar 和其他容器中都有类似这样的功能


如果你愿意,可以使用其他容器。Microsoft.Extensions.DependencyInjection只是一个易于使用且轻量级的容器。它并不是唯一的选择,如果你需要比它提供更多的功能,那么你可以自由地使用其他东西。 - Chris Pratt
https://github.com/aspnet/Extensions/tree/master/src/DependencyInjection - Chris Pratt
3个回答

6

ASP.NET Core 3.x 实际上引入了一个作用域和提供程序验证的功能。这两个功能在不同的情况下都非常有用(参见下面的帖子和示例代码)。

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    })
    .UseDefaultServiceProvider((context, options) =>
    {
        options.ValidateScopes = context.HostingEnvironment.IsDevelopment();
        options.ValidateOnBuild = true;
    });

https://andrewlock.net/new-in-asp-net-core-3-service-provider-validation/


那篇博客中对限制的描述很好。我被第四条“工厂函数没有被检查”所困扰。在我的看法中,只需对其签名进行小改动,就应该能够在不实际调用工厂函数的情况下实现它。如果工厂函数带有一个Type[] dependencies参数来声明它的依赖关系,那么就可以反射这个参数并使用常规的验证技术。 - AdamAL

1

1
我不确定这是否实际可行,因为某些服务可能会被限定范围。如果您尝试在作用域之外检索限定范围的服务,则会收到InvalidOperationException异常。因此,您需要一些方法事先检查服务的生命周期,或者在作用域内外都运行检查。 - Chris Pratt
可以创建一个作用域,在作用域内进行检查。只要满足 DI 的要求即可。 - Ahmet
这就是问题的一部分......我不想依靠自己的记忆来检查我注册的所有服务。我已经告诉容器了关于它们的信息,它应该能够验证依赖关系图。它不应该需要实例化(在某些情况下可能最好不要触发昂贵的工厂函数)。它只需要检查图形,对吗? - Anthony Mastrean
那么恐怕@ChrisPratt是正确的。期望容器调整所需的范围等将是困难的。但如果这不是问题,您可以封装您的服务注册并将服务类型保存在数组中。您只需验证该数组中的服务类型即可。我知道这不够优雅,但这是一个可能的解决方案。 - Ahmet

1

IServiceCollection 实际上是可枚举的 ServiceDescriptor,其中包含已注册服务和实现的类型信息。服务集合通常不会被注册,但在托管服务中捕获服务集合和服务提供程序应该是可能的。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddServicesValidation();
}

正确的扩展方法可以捕获服务集合,而不实际注册它(这感觉更“安全”)。
    public static class ValidateServicesExtensions
    {
        public static IServiceCollection AddServicesValidation(this IServiceCollection services)
        {
            services.AddHostedService<ValidateServices>(provider => new ValidateServices(services, provider));
            return services;
        }
    }

现在,托管服务可以迭代注册的服务和实现。虽然,在第一个通用的 IOptions<TOption> 上出现了错误,但我相信我们可以想出些解决方案?

ValidateServices.cs

    public class ValidateServices : BackgroundService
    {
        private readonly IServiceCollection services;
        private readonly IServiceProvider provider;

        public ValidateServices(
            IServiceCollection services,
            IServiceProvider provider
        )
        {
            this.services = services;
            this.provider = provider;
        }

        protected override Task ExecuteAsync(CancellationToken stoppingToken)
        {
            using var scope = provider.CreateScope();

            foreach (var service in services)
            {
                _ = scope.ServiceProvider.GetRequiredService(service.ServiceType);
            }

            return Task.CompletedTask;
        }
    }

除了泛型之外,它运行得非常好。 - gilliduck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接