.NET 9中IKeyedServiceProvider为何需手动注册至DI容器?
你遇到的这个情况确实挺让人困惑的——明明ServiceProvider实现了IKeyedServiceProvider,官方文档也提到了这一点,但就是不能直接注入。我来帮你理清楚这里面的细节:
1. 报错的核心原因
虽然ServiceProvider本身是IKeyedServiceProvider的实现类,但.NET 9的DI容器并没有把IKeyedServiceProvider加入到自动注册的服务列表中。容器默认只会自动注册IServiceProvider作为可注入服务,所以当你在构造函数里要求注入IKeyedServiceProvider时,容器找不到对应的服务条目,自然就抛出了解析失败的异常。
2. 手动注册可行的原因
你添加的那句手动注册代码:
builder.Services.AddSingleton(provider => (IKeyedServiceProvider)provider);
其实是把当前的ServiceProvider实例(本身就实现了IKeyedServiceProvider),以IKeyedServiceProvider的类型重新注册到容器里。这样容器就能识别并解析这个类型了,这是一个完全合法的临时解决方案,尽管它看起来“多此一举”。
3. 关于已修复的Issue #90528
你提到的那个GitHub Issue其实解决的是另一个问题:它让IServiceProvider具备了调用GetKeyedService方法获取键控服务的能力,但并没有解决IKeyedServiceProvider本身的自动注册问题。所以即使那个Issue被修复了,你现在遇到的注入问题依然存在——这是两个独立的功能点。
4. 当前.NET 9的预期行为
根据.NET 9的当前设计,IKeyedServiceProvider确实需要手动注册才能注入,这和IServiceProvider的自动可用行为形成了明显的不一致,也和文档描述给人的直觉不符。不过社区已经多次反馈这个问题,官方团队大概率会在后续的.NET版本(比如.NET 10)中调整这个设计,让IKeyedServiceProvider也能像IServiceProvider一样自动被容器识别。
5. 不需要手动注册的替代方案
如果你不想添加额外的注册代码,也可以通过注入IServiceProvider,然后在代码中将其强制转换为IKeyedServiceProvider来使用,效果是一样的:
[ApiController] [Route("[controller]")] public class CatalogController(CatalogDbContext context, IServiceProvider serviceProvider) : ControllerBase { [HttpGet] public async Task<IActionResult> Get() { var keyedProvider = (IKeyedServiceProvider)serviceProvider; var service = keyedProvider.GetRequiredKeyedService<IProductService>("ProductService1"); var products = await context.Products.ToListAsync(); return Ok(products); } }
内容来源于stack exchange

