如何在AddControllers方法中获取DI容器解析格式化器依赖
如何正确注册依赖DI的自定义输入/输出格式化器
你不需要在AddControllers的配置委托中强行获取DI容器实例,这个阶段根服务容器还未完成构建,直接在这里取服务很容易出现生命周期错误、容器重复构建等问题。
推荐实现:基于IConfigureOptions<MvcOptions>配置
这是ASP.NET Core官方推荐的MVC配置方式,完全符合DI生命周期规范,所有依赖会由容器自动解析,不需要手动实例化对象,也不需要用临时变量捕获引用。
- 首先正常将自定义格式化器注册到DI容器,生命周期根据你的业务需求选择即可
- 注册一个
IConfigureOptions<MvcOptions>的实现类,在这个类中通过构造函数直接注入你需要的格式化器实例(DI会自动解析格式化器的所有构造函数依赖) - 在
Configure方法中完成输入、输出格式化器的清理和注册逻辑
完整实现代码如下:
var builder = WebApplication.CreateBuilder(args); // 注册自定义格式化器到DI容器 builder.Services.AddTransient<TextInputFormatter, MyInputFormatter>(); builder.Services.AddTransient<TextOutputFormatter, MyOutputFormatter>(); // 注册MVC配置类 builder.Services.AddTransient<IConfigureOptions<MvcOptions>, CustomMvcFormatterConfig>(); // 正常注册控制器,不需要在这里写格式化器配置逻辑 builder.Services.AddControllers(); var app = builder.Build(); app.Run();
配置类实现:
public class CustomMvcFormatterConfig : IConfigureOptions<MvcOptions> { private readonly TextInputFormatter _myInputFormatter; private readonly TextOutputFormatter _myOutputFormatter; // 构造函数直接注入格式化器,所有依赖由DI自动解析 public CustomMvcFormatterConfig(TextInputFormatter myInputFormatter, TextOutputFormatter myOutputFormatter) { _myInputFormatter = myInputFormatter; _myOutputFormatter = myOutputFormatter; } public void Configure(MvcOptions options) { options.InputFormatters.Clear(); options.OutputFormatters.Clear(); options.InputFormatters.Add(_myInputFormatter); options.OutputFormatters.Add(_myOutputFormatter); } }
其他实现方式的问题说明
- 提前声明
app变量通过闭包捕获引用的写法:虽然可以运行,但代码耦合度高,依赖闭包执行时机的巧合,很容易因为后续代码调整触发空引用异常,不属于规范实现。 - 直接手动
new格式化器实例的写法:如果格式化器存在多层依赖,需要手动逐层传递所有构造参数,后续依赖调整时需要同步修改实例化代码,维护成本极高,完全失去DI容器的价值。 - 不要在
AddControllers委托内调用builder.BuildServiceProvider()获取服务:这个操作会单独构建一套独立的DI容器,导致单例服务被重复创建,是官方明确禁止的反模式。
内容的提问来源于stack exchange,提问作者RobIII
相关产品推荐
相关产品推荐

