关于ASP.NET Core中AddTransient依赖注入的两个技术疑问
问题解答
1. 为什么Transient生命周期未按预期工作?
你注册的AddTransient<IWeatherForecast, WeatherForecast>配置是正确的,问题出在控制器的Get方法实现逻辑上:
var returnValue= Enumerable.Range(1, 5).Select(index => _weatherForecast).ToArray();
这里的_weatherForecast是控制器构造时DI容器注入的单个实例,你在循环中只是重复返回这个已有的对象,并没有每次从DI容器中请求新的Transient实例。
Transient的核心规则是:每次向DI容器请求服务时,都会创建一个全新的实例。而你当前的逻辑是复用了控制器初始化时拿到的那一个实例,自然所有返回项的随机数都完全相同。
修正方法
要获取5个不同的Transient实例,你需要每次都从DI容器中请求新实例。推荐两种实现方式:
方式一:注入IServiceProvider直接获取
修改控制器代码:
using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; namespace WebApplication2.Controllers; [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IServiceProvider _serviceProvider; public WeatherForecastController(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<IWeatherForecast> Get() { return Enumerable.Range(1, 5) .Select(_ => _serviceProvider.GetRequiredService<IWeatherForecast>()) .ToArray(); } }
方式二:使用工厂模式(更符合依赖注入原则)
- 在Program.cs中注册工厂:
builder.Services.AddTransient<IWeatherForecast, WeatherForecast>(); builder.Services.AddTransient<Func<IWeatherForecast>>(sp => sp.GetRequiredService<IWeatherForecast>);
- 控制器注入工厂并使用:
using Microsoft.AspNetCore.Mvc; using System; using System.Collections.Generic; using System.Linq; namespace WebApplication2.Controllers; [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly Func<IWeatherForecast> _weatherForecastFactory; public WeatherForecastController(Func<IWeatherForecast> weatherForecastFactory) { _weatherForecastFactory = weatherForecastFactory; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<IWeatherForecast> Get() { return Enumerable.Range(1, 5) .Select(_ => _weatherForecastFactory()) .ToArray(); } }
两种方式都能保证每次获取新的Transient实例,返回结果的随机数就会各不相同。
2. DI容器如何知晓控制器的依赖需求?
ASP.NET Core的DI系统通过构造函数注入机制解析依赖:
- 当HTTP请求到达时,框架需要实例化对应的
WeatherForecastController来处理请求。 - 框架会通过反射检查控制器的构造函数,发现它需要一个
IWeatherForecast类型的参数。 - 随后框架向DI容器请求解析
IWeatherForecast服务:如果该服务已通过AddTransient等方法注册,容器会创建对应的实例并注入到控制器中;如果没有注册,就会抛出你看到的InvalidOperationException错误。
简单来说,DI容器通过分析控制器的构造函数参数列表,识别它需要的依赖服务,这是构造函数注入的核心工作原理。
相关代码参考
WeatherForecastController.cs(原代码)
using Microsoft.AspNetCore.Mvc; namespace WebApplication2.Controllers; [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private readonly IWeatherForecast _weatherForecast; public WeatherForecastController(IWeatherForecast weatherForecast) { _weatherForecast = weatherForecast; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<IWeatherForecast> Get() { var returnValue= Enumerable.Range(1, 5).Select(index => _weatherForecast).ToArray(); return returnValue; /* nearly original one, working as expected return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateTime.Now.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = "testing" }) .ToArray(); */ } }
WeatherForecast.cs
namespace WebApplication2; public class WeatherForecast : IWeatherForecast { public WeatherForecast() { Date = DateTime.Now.AddDays(0); TemperatureC = Random.Shared.Next(-20, 55); Summary = "testing"; } public DateTime Date { get; set; } public int TemperatureC { get; set; } public string? Summary { get; set; } public int TemperatureF() => 32 + (int)(TemperatureC / 0.5556); } public interface IWeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public string? Summary { get; set; } public int TemperatureF(); }
Program.cs注册代码
... builder.Services.AddTransient<IWeatherForecast, WeatherForecast>(); ...
内容的提问来源于stack exchange,提问作者Soner from The Ottoman Empire
相关产品推荐
相关产品推荐

