Add XCode skills for entity caching, ORM, and sharding ETL
|
---
name: dependency-injection-ioc
description: >
使用 NewLife.Core 内置轻é‡çº§ IoC 容器(ObjectContainer)进行ä¾èµ–注入,
涵盖æœåŠ¡ç”Ÿå‘½å‘¨æœŸæ³¨å†Œï¼ˆSingleton/Transient/Scoped)ã€å…¨å±€å·¥åŽ‚è®¿é—®ã€
工厂委托注册ã€Add vs TryAdd è¯ä¹‰ï¼Œä»¥åŠä¸Ž ASP.NET Core DI 的两阶段集æˆã€‚
适用于应用å¯åЍé…ç½®ã€è·¨ç»„件解耦ã€IoC 容器桥接与代ç 审查任务。
argument-hint: >
è¯´æ˜Žä½ çš„ DI 场景:纯控制å°/Worker 应用还是 ASP.NET Coreï¼›
需è¦ç”¨ AddSingleton/AddTransient/AddScoped;是å¦éœ€è¦å·¥åŽ‚å§”æ‰˜æ³¨å†Œï¼›
是å¦è¦ä¸Ž ASP.NET Core 内置 DI 容器è”动(SetInnerProvider)。
---
# ä¾èµ–注入与 IoC 容器技能
## 适用场景
- 新建应用需è¦ç®€å•çš„ DIï¼Œä¸æƒ³ä¾èµ– ASP.NET Core 的完整 DI 框架。
- 库或组件注册自身æœåŠ¡åˆ°å…¨å±€å®¹å™¨ï¼ˆ`ObjectContainer.Current`),供宿主应用解æžã€‚
- 在 ASP.NET Core 应用ä¸ï¼Œéœ€è¦å°† `ObjectContainer` 与内置容器桥接,使两套容器共享注册。
- 代ç 审查:确认æœåŠ¡ç”Ÿå‘½å‘¨æœŸé€‰æ‹©æ°å½“,é¿å… Scoped æœåŠ¡æ³¨å…¥ Singleton,é¿å… `new` 代替注入。
## æ ¸å¿ƒåŽŸåˆ™
1. **全局容器唯一入å£**:`ObjectContainer.Current`(注册时使用)和 `ObjectContainer.Provider`ï¼ˆè§£æžæ—¶ä½¿ç”¨ï¼‰æ˜¯åº”用级全局å•例,库代ç 通过它们读写æœåŠ¡ï¼Œä¸ä¼ 递容器实例。
2. **`TryAdd` vs `Add` è¯ä¹‰å·®å¼‚**:`TryAdd` 仅在该æœåŠ¡ç±»åž‹å°šæœªæ³¨å†Œæ—¶æ‰æ·»åŠ ï¼ˆåº“ä»£ç æ³¨å†Œé»˜è®¤å®žçŽ°ç”¨æ¤æ¨¡å¼ï¼‰ï¼›`Add` å…许åŒç±»åž‹é‡å¤æ³¨å†Œï¼ˆåŒä¸€ç±»åž‹å¤šå®žä¾‹åœºæ™¯ï¼Œå¦‚多个 `IHostedService`)。
3. **生命周期选择**:`Singleton` 共享全程;`Transient` æ¯æ¬¡éƒ½ `new`ï¼›`Scoped` 仅在 Web 场景æ¯è¯·æ±‚共享——纯控制å°åº”用一般ä¸éœ€è¦ Scoped。
4. **æž„é€ å‡½æ•°è‡ªåŠ¨æ³¨å…¥**:`ObjectContainer` ä¼šè‡ªåŠ¨åˆ†æžæž„é€ å‡½æ•°å‚æ•°ï¼Œé€‰æ‹©æ‰€æœ‰å‚数都已注册的é‡è½½ï¼›è‹¥æž„é€ å‡½æ•°æœ‰æœªæ³¨å†Œå‚æ•°ï¼Œè¯·ç”¨å·¥åŽ‚å§”æ‰˜æ‰‹åŠ¨æä¾›ã€‚
5. **两阶段åˆå§‹åŒ–(ASP.NET Core)**:`ObjectContainer.Current` 先注册,应用构建åŽç”¨ `SetInnerProvider(app.Services)` æ¡¥æŽ¥ï¼Œä¹‹åŽ `ObjectContainer.Provider` 的解æžè¯·æ±‚会委托给真æ£çš„ `IServiceProvider`。
## 执行æ¥éª¤
### 一ã€åŸºç¡€æ³¨å†Œä¸Žè§£æž
```csharp
var ioc = ObjectContainer.Current;
// Singletonï¼šæ•´ä¸ªåº”ç”¨ç”Ÿå‘½å‘¨æœŸåªæœ‰ä¸€ä¸ªå®žä¾‹
ioc.AddSingleton<IAppConfig, AppConfig>();
// Transientï¼šæ¯æ¬¡ GetService 都创建新实例
ioc.AddTransient<IUserService, UserService>();
// 注册已有实例(常用于é…置对象)
ioc.AddSingleton<DatabaseOptions>(new DatabaseOptions { ConnStr = "..." });
// è§£æž
var config = ObjectContainer.Provider.GetService<IAppConfig>();
var userSvc = ObjectContainer.Provider.GetRequiredService<IUserService>();
```
### 二ã€å·¥åŽ‚å§”æ‰˜æ³¨å†Œ
```csharp
// 需è¦åœ¨åˆ›å»ºæ—¶è®¿é—®å…¶ä»–æœåŠ¡æ—¶ï¼Œä½¿ç”¨å·¥åŽ‚å§”æ‰˜
ioc.AddSingleton<IDbConnectionFactory>(sp =>
{
var opts = sp.GetRequiredService<DatabaseOptions>();
return new SqlConnectionFactory(opts.ConnStr);
});
// Transient 工厂
ioc.AddTransient<ICommand>(sp =>
{
var log = sp.GetRequiredService<ILog>();
return new SyncCommand(log, retryCount: 3);
});
```
### 三ã€TryAdd ä¿æŠ¤é»˜è®¤å®žçŽ°
```csharp
// 库代ç :åªåœ¨ç”¨æˆ·æœªæ‰‹åŠ¨æ³¨å†Œæ—¶æä¾›é»˜è®¤å®žçް
ioc.TryAddSingleton<ITracer, DefaultTracer>();
ioc.TryAddSingleton<ICache, MemoryCache>();
// 应用代ç å¯è¦†ç›–:调用时机必须在库注册之å‰ï¼Œæˆ–在 TryAdd 之åŽç”¨ Add 覆盖
ioc.AddSingleton<ITracer>(myCustomTracer); // Add ä¸å—ä¿æŠ¤ï¼Œç›´æŽ¥è¿½åŠ
```
### å››ã€å¤šå®žçŽ°æ³¨å†Œï¼ˆå¦‚ IHostedService)
```csharp
// Addï¼ˆè€Œéž TryAdd)å¯ä»¥ä¸ºåŒä¸€æŽ¥å£æ³¨å†Œå¤šä¸ªå®žçް
ioc.Add(new ServiceDescriptor(typeof(IHostedService), typeof(MetricsService), ObjectLifetime.Singleton));
ioc.Add(new ServiceDescriptor(typeof(IHostedService), typeof(CleanupService), ObjectLifetime.Singleton));
// è§£æžï¼šGetServices<T>(扩展方法)返回所有注册实现
var services = ObjectContainer.Provider.GetServices<IHostedService>();
```
### 五ã€ASP.NET Core åŒå®¹å™¨æ¡¥æŽ¥
```csharp
// Program.cs — 注册阶段(AddXxx 时)
var ioc = ObjectContainer.Current;
ioc.AddSingleton<ICustomService, CustomService>(); // 注册到 NewLife 容器
// 也å¯ä»¥å°† IServiceCollection 委托到 ObjectContainer
builder.Services.AddFromObjectContainer(ioc); // è‹¥æœ‰æ¤æ‰©å±•
// 构建完æˆåŽï¼Œæ¡¥æŽ¥åˆ° ASP.NET Core 内置 DI
var app = builder.Build();
ObjectContainer.SetInnerProvider(app.Services);
// ä¹‹åŽ ObjectContainer.Provider.GetService<T>()
// 会先查 NewLife è‡ªèº«ï¼Œå†æŸ¥ app.Services(内部æä¾›è€…)
```
### å…ã€Scoped 作用域(Web 请求)
```csharp
// 注册 Scoped
ioc.AddScoped<IDbContext, AppDbContext>();
// 在ä¸é—´ä»¶ä¸æ‰‹åŠ¨åˆ›å»ºä½œç”¨åŸŸï¼ˆè‹¥ä¸ç”¨ IServiceScopeFactory 自动处ç†ï¼‰
using var scope = ObjectContainer.Provider.CreateScope();
var dbCtx = scope.ServiceProvider.GetRequiredService<IDbContext>();
await dbCtx.SaveChangesAsync();
// scope.Dispose() → dbCtx 被释放
```
## é‡ç‚¹æ£€æŸ¥é¡¹
- [ ] Scoped æœåŠ¡æ˜¯å¦è¢«æ³¨å…¥åˆ° Singleton æœåŠ¡ä¸ï¼ˆæ•获ä¾èµ–问题,导致 Scoped 退化为 Singleton)?
- [ ] åº“ä»£ç æ˜¯å¦ä½¿ç”¨ `TryAdd` è€Œéž `Add` 注册默认实现(é¿å…覆盖用户自定义)?
- [ ] `GetRequiredService` 在æœåŠ¡æœªæ³¨å†Œæ—¶ä¼šæŠ›å‡ºï¼Œæ˜¯å¦æœ‰æ„料外的 `InvalidOperationException`?
- [ ] `SetInnerProvider` 是å¦åœ¨ `app.Build()` 之åŽè°ƒç”¨ï¼Œè€Œä¸æ˜¯åœ¨æ³¨å†Œé˜¶æ®µï¼Ÿ
- [ ] Transient æœåŠ¡å®žçŽ°äº† `IDisposable` 时,是å¦ç”±å®¹å™¨åœ¨æ£ç¡®æ—¶æœºé‡Šæ”¾ï¼ˆè¿˜æ˜¯æ³„æ¼ï¼‰ï¼Ÿ
- [ ] æ˜¯å¦æœ‰ç»•过 DI 直接 `new` æœåŠ¡å®žä¾‹çš„ä»£ç ,导致其ä¾èµ–未被注入?
## è¾“å‡ºè¦æ±‚
- **接å£**:`IObjectContainer`(注册 API)ã€`IServiceProvider`ï¼ˆè§£æž APIï¼Œæ ‡å‡†å…¼å®¹ï¼‰ã€`IObject`/`ServiceDescriptor`(注册元数æ®ï¼‰ã€‚
- **生命周期**:`ObjectLifetime.Singleton/Transient/Scoped` 枚举。
- **全局入å£**:`ObjectContainer.Current`(注册)ã€`ObjectContainer.Provider`(解æžï¼‰ã€‚
- **扩展方法**:`AddSingleton<T,TImpl>()`/`AddTransient<T,TImpl>()`/`AddScoped<T,TImpl>()`(æµå¼ API)。
- **测试**:测试时å¯ä¸ºæ¯ä¸ªæµ‹è¯•用例创建独立 `new ObjectContainer()`,é¿å…å…¨å±€çŠ¶æ€æ±¡æŸ“。
## å‚考资料
å‚考示例与模å¼è¯æ®è§ `references/newlife-ioc-patterns.md`。
|