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