商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C#如何配置依赖注入_C#在控制台程序中使用DI容器【架构】

C#如何配置依赖注入_C#在控制台程序中使用DI容器【架构】

  发布于2026-07-08 阅读(0)

扫一扫,手机访问

控制台程序里确实用不上 ASP.NET Core 自带的 HTTP 请求作用域,但 IServiceCollection 搭配 ServiceProvider 这套组合拳完全能打。说到底,问题不在于“能不能用”,而在于“怎么配才不踩坑”。下面把这些常见的坑和正确的做法捋一遍。

注册服务前,先确认项目已引用 Hosting 包

控制台项目默认不带 DI 容器运行时支持,光装个 Microsoft.Extensions.DependencyInjection 是远远不够的。必须加上 Microsoft.Extensions.Hosting(.NET 6 及以上版本推荐),或者至少搭配 Microsoft.Extensions.DependencyInjection.Abstractions 并手动调用 BuildServiceProvider()

  • 项目文件里需要包含这样一行:(版本号要和 SDK 对齐)
  • 没装这个包的话,Host.CreateApplicationBuilder() 一跑就会报找不到类型;硬着头皮用 new ServiceCollection() 虽然也能注册,但缺了 Host 环境下自动配置的日志、配置等扩展能力,等于自己给自己找麻烦
  • 特别提醒:别在 .NET Framework 项目里硬套这套——Microsoft.Extensions.* 是 .NET Core 及之后生态的东西,Framework 下需要额外做适配

AddTransient/Scoped/Singleton 在控制台里怎么选

控制台程序没有 Web 项目那种“每个请求一个作用域”的自动机制,所以 AddScoped 的行为会变得有点特别——它只在你手动 CreateScope() 之后才生效,否则,它就等同于 AddTransient

  • AddTransient():每次调用 GetRequiredService() 都新建一个实例;适合无状态工具类,比如 IMapperIEmailSender 这类
  • AddScoped():必须搭配 using var scope = provider.CreateScope(); 来使用,否则注入 IRepository 时会直接报错,提示“没有注册 IRepository 类型的服务”
  • AddSingleton():这个最安全,IConfigurationILoggerFactory 这类全局只读对象就该用单例。但有一个典型陷阱:千万别让 Singleton 类持有 Scoped 服务,比如把 DbContext 塞进单例类里,启动时就会报“无法从根作用域解析 Scoped 服务”

构造函数注入只对容器创建的对象生效

这是一个很容易被忽略的规则:你在 Main 里直接 new MessageService(new ConsoleMessageWriter()),哪怕 MessageService 的构造函数声明了 IMessageWriter,DI 也完全不参与——那个字段永远是 null,不会自动填充。

  • 正确做法:先注册 MessageService(比如 builder.Services.AddTransient()),然后通过 provider.GetRequiredService() 来获取
  • 中间件、后台服务、定时任务回调里想用服务?不能靠字段注入。正确做法是把 IServiceProvider 注入到宿主类中(比如 Worker : BackgroundService),然后调用 _provider.CreateScope() 来获取新作用域
  • 接口和实现类型必须严格匹配:注册了 AddScoped(),构造函数里就得写 IEmailService email,写成 SmtpEmailService email 就会报“没有注册 SmtpEmailService 类型的服务”

非托管上下文必须手动管理作用域生命周期

Timer 回调、BackgroundService.ExecuteAsync、单元测试这些地方,没有现成的 IScopedService 上下文,直接 Resolve Scoped 服务肯定会炸。

  • 错误写法:var repo = provider.GetRequiredService(); —— 会报 No service for type 'IRepository'
  • 正确写法:using var scope = provider.CreateScope(); var repo = scope.ServiceProvider.GetRequiredService();
  • 必须用 using 包裹作用域,否则 DbContext 不会释放,可能导致连接池耗尽、内存泄漏。另外,千万别把 scope.ServiceProvider 存成字段长期持有——它只在当前 using 块内有效
  • 单元测试常用轻量方案:new ServiceCollection().AddScoped().BuildServiceProvider()

还有一个容易被忽略的细节:注册顺序会影响替换逻辑。用 TryAddReplace 时,如果第三方库内部已经注册过某个服务(比如 EF 的 IDbContextFactory),你的 AddScoped 可能被静默跳过——要发现这个问题,要么去看源码,要么启用 DI 日志。

本文转载于:https://www.php.cn/faq/2420267.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注