.NET 6依赖注入循环问题:同列表注册依赖解析失败原因探究
我正在尝试理解导致DI容器在构造依赖同列表中其他注册项的服务时出现循环的根本问题。以下是简化后的代码:
核心代码
public class ImplementationFactory { private readonly IEnumerable<IImplementation> _implementations; public ImplementationFactory(IEnumerable<IImplementation> implementations) { _implementations = implementations; } public IEnumerable<IImplementation> GetAll(List<string> names) { return _implementations.Where(i => names.Contains(i.Name)) ?? throw new NotSupportedException(); } } public static class ImplementationNames { public const string ImplementationOne = "One"; public const string ImplementationTwo = "Two"; } public interface IImplementation { string Name { get; } } public class ImplementationOne : IImplementation { public string Name { get; } public ImplementationOne(string name) { Name = name; } } public class ImplementationTwo : IImplementation { public string Name { get; } public ImplementationTwo(string name) { Name = name; } }
测试代码(触发循环依赖)
public class DependencyInjectionTests { [Fact] public void CheckSecondImplementationCanLoadFirstImplementationUsingFactory() { try { var collection = new ServiceCollection(); collection.AddTransient<IImplementation, ImplementationOne>(x => new ImplementationOne(ImplementationNames.ImplementationOne)); collection.AddTransient<ImplementationFactory>(); collection.AddTransient<IImplementation, ImplementationTwo>(x => { // 此处引发循环依赖 var loadedImplementation = x.GetRequiredService<ImplementationFactory>() .GetAll(new List<string> {ImplementationNames.ImplementationOne}); return new ImplementationTwo(ImplementationNames.ImplementationTwo); }); var provider = collection.BuildServiceProvider(validateScopes: true); provider.GetRequiredService<ImplementationFactory>() .GetAll(new List<string> {ImplementationNames.ImplementationTwo}); } catch (Exception e) { Console.WriteLine(e); throw; } } }
这是现有代码库中复杂DI解析问题的简化示例。我知道可以通过实现不同接口或工厂来解决,但想了解DI容器如何尝试解析第二个实现,以及为何在工厂委托中解析第一个注册项时会失败。
1. DI容器解析ImplementationFactory的核心逻辑
当容器创建ImplementationFactory实例时,必须注入IEnumerable<IImplementation>——这意味着容器要一次性解析所有已注册的IImplementation实现,也就是ImplementationOne和ImplementationTwo。
2. 循环依赖链的形成
解析ImplementationTwo时,它的注册委托里调用了x.GetRequiredService<ImplementationFactory>(),但此时ImplementationFactory还未完成创建(它正等待ImplementationTwo被解析后注入到IEnumerable<IImplementation>中),于是形成闭环:
ImplementationFactory→ 需要IEnumerable<IImplementation>→ 需要ImplementationTwo→ 需要ImplementationFactory
3. 单独解析ImplementationOne无问题的原因
ImplementationOne的注册委托不依赖任何其他服务,容器可以直接实例化它。但一旦ImplementationFactory需要所有IImplementation实现,容器就必须处理包含依赖的ImplementationTwo,触发循环。
4. validateScopes: true的放大作用
启用这个参数后,容器在构建ServiceProvider时就会提前验证服务的解析可行性,直接在构建阶段抛出循环依赖错误,而不是等到调用服务时才暴露问题。
内容的提问来源于stack exchange,提问作者Appa

