依赖注入注册是消费者使用连接器的唯一入口。本节约定扩展方法如何命名、Provider 注册成什么生命周期、单 Provider 与多 Provider 场景的区别,以及聚合 .All 包如何根据配置节存在性驱动注册。
扩展方法命名:两种风格
所有 DI 扩展方法注册在 Microsoft.Extensions.DependencyInjection 命名空间下,消费者 using 后即可链式调用。
| 风格 | 公式 | 示例 | 适用域 |
|---|---|---|---|
| 推荐 | AddBitzsoft{Vendor}{Domain}() | AddBitzsoftStripePayment() | 除 Sms 外全部 |
| 遗留 | Add{Vendor}Sms() | AddAliyunSms() | Sms |
| 聚合 | AddBitzsoft{Domain}All() | AddBitzsoftPaymentAll() | 所有 .All 包 |
推荐风格加 Bitzsoft 前缀,避免与其他集成库的同名扩展方法冲突。遗留 Sms 域因历史原因不加前缀,暂不改造。
Provider 生命周期
核心原则:HTTP Provider 用 Transient
AddIntegrationProviderCapability<TCapability, TImplementation>() 是注册 Provider 的标准 API,默认 ServiceLifetime.Transient。这是核心约定——HTTP Provider 不应是 Singleton,因为 typed HttpClient 不应被长生命周期对象捕获。
// Core 包提供的标准注册 APIpublic static IServiceCollection AddIntegrationProviderCapability<TCapability, TImplementation>( this IServiceCollection services, IntegrationProviderDescriptor descriptor, ServiceLifetime implementationLifetime = ServiceLifetime.Transient, // ① 默认 Transient bool registerLegacyAlias = true) // ② 单 Provider 兼容 where TCapability : class where TImplementation : class, TCapability各域生命周期分布
| 域 | 生命周期 | 注册方式 | 原因 |
|---|---|---|---|
| Payment | Transient | AddIntegrationProviderCapability | 默认,标准模式 |
| TeamWork | Transient | AddIntegrationProviderCapability | 避免 Singleton 捕获 typed HttpClient |
| OutboundCall | Transient | AddIntegrationProviderCapability | 默认 |
| Finance | Transient | AddIntegrationProviderCapability | 默认 |
| FileStorage | Singleton | AddIntegrationProviderCapability(..., Singleton) | 底层 SDK 客户端有状态且构造昂贵 |
| EnterpriseMail.NetEase | Singleton | TryAddSingleton<NetEaseMailClient>() | MailKit 客户端有状态(连接池) |
| Sms | Transient | AddTransient<ISMS, AliyunSmsService>() | 遗留,直接 AddTransient |
Provider 构造函数的工厂委托
Provider 构造函数是 internal,DI 容器无法直接激活。注册时用工厂委托显式构造,绕过可见性限制:
services.AddIntegrationProviderCapability<IPaymentProvider, StripePaymentProvider>( StripeProviderDescriptor.Instance, serviceProvider => new StripePaymentProvider( // ① 工厂委托显式 new serviceProvider.GetRequiredService<StripeHttpClient>(), serviceProvider.GetRequiredService<IStripeConfigProvider>(), serviceProvider.GetRequiredService<ILogger<StripePaymentProvider>>()));单 Provider 兼容 vs 多 Provider 路由
registerLegacyAlias 参数控制 Provider 注册到能力接口的方式,决定了消费者如何解析 Provider。
单 Provider 模式(registerLegacyAlias = true)
默认 true,Provider 同时注册为 TImplementation(按 ID 索引)和能力接口 TCapability(直接可注入)。消费者可直接注入能力接口,无需 Resolver:
// DI 注册(默认 registerLegacyAlias = true)services.AddBitzsoftStripePayment(options => { ... });
// 消费者——直接注入 IPaymentProviderpublic class CheckoutService(IPaymentProvider payment) // ① 单 Provider,直接注入{ public async Task Pay() => await payment.CreateOrderAsync(...);}多 Provider 模式(registerLegacyAlias = false)
注册多个同类 Provider 时,能力接口的直接注入会产生歧义。此时用 IIntegrationProviderResolver<TCapability> 按稳定 Provider ID 解析:
// DI 注册——多个支付渠道,禁用 legacy aliasservices.AddBitzsoftStripePayment(options => { ... });services.AddBitzsoftAlipayPayment(options => { ... });
// 消费者——用 Resolver 按需解析public class CheckoutService(IIntegrationProviderResolver<IPaymentProvider> resolver){ public async Task Pay(string providerId) { // ① 按稳定 ID 解析 Provider var provider = resolver.GetRequired(providerId); // "stripe" 或 "alipay" await provider.CreateOrderAsync(...); }}Stripe 的混合用法
Stripe 注册了两个能力(支付 + Webhook 验签),其中 Webhook 验签显式禁用 legacy alias 避免与支付接口冲突:
services.AddIntegrationProviderCapability<IPaymentProvider, StripePaymentProvider>( StripeProviderDescriptor.Instance, serviceProvider => new StripePaymentProvider(...)); // ① registerLegacyAlias 默认 true——支付能力可直接注入
services.AddIntegrationProviderCapability<IWebhookVerifier, StripeWebhookVerifier>( StripeProviderDescriptor.Instance, serviceProvider => new StripeWebhookVerifier(...), registerLegacyAlias: false); // ② 禁用——避免与其它厂商 WebhookVerifier 冲突聚合 .All 包注册
.All 包是空程序集,仅含一个 ServiceCollectionExtensions,按配置节存在性驱动注册。消费者只需调用一次 AddBitzsoft{Domain}All(),传入 IConfiguration,包内自动注册配置中存在的所有厂商。
工作原理
public static IServiceCollection AddBitzsoftPaymentAll( this IServiceCollection services, IConfiguration configuration, string sectionKey = "Payment"){ var section = configuration.GetSection(sectionKey);
// ① 配置节存在才注册——GetSection().Exists() 检查 if (section.GetSection("Alipay").Exists()) services.AddBitzsoftAlipayPayment(configuration, $"{sectionKey}:Alipay");
if (section.GetSection("WeChatPay").Exists()) services.AddBitzsoftWeChatPayPayment(configuration, $"{sectionKey}:WeChatPay");
if (section.GetSection("Stripe").Exists()) services.AddBitzsoftStripePayment(configuration, $"{sectionKey}:Stripe");
return services;}FileStorage.All 的泛型封装
FileStorage 有 12 个厂商,重复的 if (Exists()) 样板被提取成私有辅助方法:
RegisterIfPresent( section, "Aliyun", configure => services.AddBitzsoftAliyunOssFileStorage(configure));RegisterIfPresent( section, "Aws", configure => services.AddBitzsoftAwsS3FileStorage(configure));// ... 其余 10 个厂商
// 辅助方法static void RegisterIfPresent( IConfiguration section, string providerName, Action<IConfigurationSection> register){ var provider = section.GetSection(providerName); if (provider.Exists()) register(provider);}完整注册示例
单 Provider 场景
var builder = WebApplication.CreateBuilder(args);
// ① 注册 Core(Provider 目录与解析器)builder.Services.AddBitzsoftIntegrationCore();
// ② 注册单个支付渠道——可直接注入 IPaymentProviderbuilder.Services.AddBitzsoftStripePayment(builder.Configuration.GetSection("Payment:Stripe"));
var app = builder.Build();
// 消费app.MapPost("/pay", async (IPaymentProvider payment) => await payment.CreateOrderAsync(new PaymentOrderRequest { ... }));多 Provider 场景
var builder = WebApplication.CreateBuilder(args);
// ① 注册 Corebuilder.Services.AddBitzsoftIntegrationCore();
// ② 聚合注册——配置中存在的厂商全部注册builder.Services.AddBitzsoftPaymentAll(builder.Configuration);
var app = builder.Build();
// 消费——用 Resolver 按稳定 ID 解析app.MapPost("/pay", async ( string providerId, IIntegrationProviderResolver<IPaymentProvider> resolver) =>{ var provider = resolver.GetRequired(providerId); // "stripe" / "alipay" / "wechatpay" return await provider.CreateOrderAsync(new PaymentOrderRequest { ... });});对应的 appsettings.json
{ "Payment": { "Stripe": { "SecretKey": "sk_live_xxx", "WebhookSecret": "whsec_xxx" }, "Alipay": { "AppId": "2021xxx", "PrivateKey": "..." } }}注册内容清单
一个完整的厂商注册通常包含四项:
以 Stripe 为例,这四项的对应代码:
public static IServiceCollection AddBitzsoftStripePayment( this IServiceCollection services, Action<StripeOptions> configure){ var options = new StripeOptions(); configure(options);
services.AddHttpClient(options.HttpClientName).AddRequestLogging(options.HttpClientName); // ① HttpClient services.Configure(configure); // ② Options services.TryAddSingleton<IStripeConfigProvider, StripeConfigProvider>(); // ③ ConfigProvider RegisterProvider(services); // ④ Provider 能力
return services;}相关约定
- HttpClient 使用约定:命名客户端注册与
AddRequestLogging细节。 - Options 配置与校验:
Configure<T>与校验器注册。 - 命名与可见性:扩展方法类与 Provider 的可见性。
- Provider 目录与解析:
IIntegrationProviderResolver工作机制。