如何在测试的ConfigureServices中向ASP.NET Core派生Startup类传入配置
这个问题的核心是你混淆了ASP.NET Core服务容器注册的依赖项和**配置系统(IConfiguration)**的作用范围——两者完全是独立的模块,这就是为什么你在TestStartup构造函数里拿不到预期的Name值的原因。
为什么你的尝试无效?
你在测试代码的ConfigureServices中通过services.AddSingleton(settings)把MySettings实例注册到了服务容器里,但TestStartup构造函数接收的IConfiguration是用来读取配置源(比如appsettings.json、环境变量、命令行参数等)的工具,它和服务容器里的实例没有任何关联。所以无论是configuration.Get<MySettings>()还是configuration.Bind(),都是从配置源里读取数据,而不是从服务容器里获取你注册的实例。
另外还有一个关键的执行顺序问题:ASP.NET Core启动时,Startup的执行流程是:
- 执行
Startup构造函数 - 执行
ConfigureServices方法(这里才会注册你的MySettings实例) - 执行
Configure方法
所以在构造函数执行时,你的settings实例还没被注册到服务容器里,就算你想从容器里拿也拿不到。
解决方案分两种场景
场景1:就是要在TestStartup中使用你注册的MySettings实例
既然你已经把实例注册到了服务容器,那就要通过依赖注入的方式获取,而不是用IConfiguration。根据执行顺序,你有两个选择:
方案A:在Configure方法中注入
Configure方法支持直接从服务容器注入依赖项,这是最推荐的方式:
public class TestStartup : Startup { public TestStartup(IConfiguration configuration) : base(configuration) { Configuration = configuration; // 这里依然拿不到,因为服务还没注册 } // 保持基类的ConfigureServices逻辑不变,或者按需重写 public override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); // 如果需要,这里可以对服务容器做额外修改,但不要直接BuildServiceProvider(除非必须) } // 在Configure方法中注入MySettings public override void Configure(IApplicationBuilder app, IWebHostEnvironment env, MySettings mySettings) { // 这里就能拿到你注册的Name="Jhon"的实例了 Console.WriteLine(mySettings.Name); // 输出Jhon base.Configure(app, env); } }
方案B:在ConfigureServices中获取(不推荐直接BuildServiceProvider)
如果你必须在ConfigureServices阶段使用这个实例,可以通过构建临时的服务提供器获取,但注意这会创建额外的服务实例,可能导致不一致:
public override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); // 构建临时服务提供器获取实例 using var sp = services.BuildServiceProvider(); var mySettings = sp.GetRequiredService<MySettings>(); Console.WriteLine(mySettings.Name); // 输出Jhon }
场景2:想让IConfiguration能读取到MySettings的配置(模拟配置文件)
如果你本来的目的是模拟appsettings.json中的MySettings配置,让IConfiguration能正确绑定到这个实例,那应该修改测试代码,把配置添加到配置源中,而不是直接注册到服务容器:
[Fact] public async Task Test() { var settings = new MySettings() { Name = "Jhon" }; var webHostBuilder = new WebHostBuilder() .UseStartup<TestStartup>() .ConfigureAppConfiguration(configBuilder => { // 添加内存配置源,这样IConfiguration就能读取到MySettings的配置了 configBuilder.AddInMemoryCollection(new Dictionary<string, string> { {"MySettings:Name", settings.Name} }); }); // 省略代码.. }
这时候你在TestStartup构造函数里用configuration.Get<MySettings>()或者configuration.Bind()就能拿到Name="Jhon"的实例了。
内容的提问来源于stack exchange,提问作者Sturla

