ASP.NET Core 6+ 在 Program.cs 中通过 builder.Services 注册服务,推荐使用 AddSingleton、AddScoped(多数业务首选)、AddTransient;构造函数须为 public;多实现需用 IEnumerable 注入;IDbConnection 应工厂创建,HttpClient 须用 AddHttpClient;自定义服务依赖应通过工厂委托从 IServiceProvider 获取。

ASP.NET Core 的 Program.cs 中如何注册服务
ASP.NET Core 6+ 默认使用精简的 Program.cs 模式,服务注册直接在 var builder = WebApplication.CreateBuilder(args) 后调用 builder.Services。这是唯一推荐的入口点,别在 Startup.cs(已弃用)或中间件里注册。
常见注册方式有三类,区别在于生命周期管理:
-
AddSingleton:整个应用生命周期共用一个实例,适合无状态工具类、配置读取器() -
AddScoped:每个 HTTP 请求创建一次,跨中间件/控制器复用,**绝大多数业务服务应选这个**() -
AddTransient:每次注入都新建实例,适合轻量、无共享状态的类(如 DTO 映射器)()
示例:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton(builder.Configuration); builder.Services.AddScoped (); builder.Services.AddTransient ();
构造函数注入是否必须 public?能否注入多个同类型服务
构造函数必须是 public,否则运行时抛出 InvalidOperationException: Unable to resolve service。.NET DI 容器只识别 public 构造函数。
同一接口注册多个实现时,默认只取最后一个——除非你明确使用集合注册:
- 用
AddScoped多次注册 → 只生效最后一次() - 改用
AddScoped+() AddScoped→ 仍只取最后注册的() - 正确做法:
builder.Services.AddScoped,然后在构造函数中接收(); builder.Services.AddScoped (); IEnumerable
这样容器会自动聚合所有已注册的 IMessageHandler 实现。
DI 容器无法解析 IDbConnection 或 HttpClient 怎么办
这些类型不能直接注册为单例或作用域服务,原因不同:
-
IDbConnection:是“即用即开即关”的资源,注册为 Scoped 会导致连接被跨请求复用,引发InvalidOperation: Connection is already in use。应改为工厂模式或在 Repository 内部用new SqlConnection(...)创建 -
HttpClient:官方强烈建议用AddHttpClient注册,而不是() AddScoped。后者易导致端口耗尽(socket exhaustion),因为() HttpClient本身是线程安全且设计为长期复用的,但底层HttpMessageHandler需要按需管理生命周期
正确写法:
builder.Services.AddHttpClient() .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { ServerCertificateCustomValidationCallback = (msg, cert, chain, err) => true });
自定义服务需要访问 IConfiguration 或 IWebHostEnvironment 怎么办
不要在构造函数里试图“手动 new 自定义服务”,这会绕过 DI 容器,导致依赖链断裂。正确方式是让容器帮你传参:
- 在注册时用工厂委托:
AddScoped(sp => new RedisCacheService(sp.GetRequiredService ())) - 工厂内可安全调用
sp.GetRequiredService<...>()获取其他已注册服务 - 注意:工厂委托中不能捕获外部变量(如局部
config变量),必须从IServiceProvider获取,否则可能拿到错误作用域的实例
特别提醒:IConfiguration 和 IWebHostEnvironment 是 Singleton,可放心在任何生命周期的服务中注入;但反过来,Scoped 服务不能注入到 Singleton 类的构造函数中,否则启动时报错。










