Skip to content
Bitzsoft.Integrationsbitzsoft.integrations

Concept

Provider 目录与解析

IntegrationProviderDescriptor、Catalog 和 Resolver 构成的跨框架多 Provider 路由体系。

Last updated

Provider 目录与解析器是处理多厂商场景的核心设计。它让消费方按稳定 Provider ID 路由到具体实现,跨 net5/net8/net10 使用同一公共 API。

核心概念

IntegrationProviderDescriptor
ID · 领域 · 能力 · 稳定性

AddIntegrationProviderCapability
注册时提交 Descriptor + 实现

IIntegrationProviderCatalog
枚举全部已注册 Provider

IIntegrationProviderResolver
按 ID 解析能力

IntegrationProviderDescriptor

每个 Provider 的可执行能力描述,是事实源:

src/Bitzsoft.Integrations.Payment.Alipay/AlipayProviderDescriptor.cs
internal static class AlipayProviderDescriptor
{
public static IntegrationProviderDescriptor Instance { get; } = new(
providerId: "Alipay",
displayName: "支付宝",
domain: "Payment",
capabilityTypes: new[] { typeof(IPaymentProvider) },
stability: IntegrationProviderStability.Preview,
vendor: "Ant Group",
apiVersion: "1.0",
authenticationType: "RSA2",
regions: new[] { "CN" },
limitations: new[] { "不支持银行卡直连" });
}

关键属性:

属性说明
ProviderId稳定、大小写不敏感的标识,发布后不可修改
ProviderKey跨领域唯一键 {Domain}:{ProviderId}
CapabilityTypes已实现并会注册到 resolver 的能力类型
StabilityExperimental / Preview / Stable / Deprecated
Regions / Environments适用地区与环境
Limitations已知限制

Supports<TCapability>() 方法判断描述是否声明指定能力:

if (!descriptor.Supports<IPaymentProvider>())
throw new ArgumentException("Descriptor does not declare this capability.");

IIntegrationProviderCatalog

枚举当前容器中所有已配置 Provider:

public interface IIntegrationProviderCatalog
{
IReadOnlyCollection<IntegrationProviderDescriptor> Providers { get; }
bool TryGet(string providerKey, out IntegrationProviderDescriptor descriptor);
bool TryGet(string domain, string providerId, out IntegrationProviderDescriptor descriptor);
IntegrationProviderDescriptor GetRequired(string providerKey);
}

IIntegrationProviderResolver<T>

按大小写不敏感的稳定 Provider ID 解析能力:

public interface IIntegrationProviderResolver<TCapability> where TCapability : class
{
IReadOnlyCollection<TCapability> Providers { get; }
bool TryResolve(string providerId, out TCapability capability);
TCapability GetRequired(string providerId);
}

注册流程

// ① 厂商包注册时提交 Descriptor + 实现
services.AddIntegrationProviderCapability<IPaymentProvider, AlipayPaymentProvider>(
AlipayProviderDescriptor.Instance,
serviceProvider => new AlipayPaymentProvider(
serviceProvider.GetRequiredService<AlipayHttpClient>(),
serviceProvider.GetRequiredService<IAlipayConfigProvider>(),
serviceProvider.GetRequiredService<ILogger<AlipayPaymentProvider>>()));

注册内部完成四件事:

  1. 提交 IntegrationProviderDescriptor 到目录(按 ProviderKey 去重)
  2. TryAdd 实现类型
  3. 添加 IntegrationProviderRegistration<T> 单例记录
  4. 在 net8+ 上额外注册 keyed service(按 ProviderId 作为 key)

消费模式

单 Provider 兼容(直接注入)

只配置一个 Provider 时,仍可直接注入能力接口:

public class CheckoutService(IPaymentProvider payment)
{
public async Task<PaymentResult<PaymentOrderResult>> PayAsync(PaymentOrderRequest req)
=> await payment.CreateOrderAsync(req);
}

这是兼容别名(registerLegacyAlias = true),用 TryAdd 注册,不覆盖已注册的生产实现。

多 Provider 路由

配置多家厂商时,必须使用 resolver:

public sealed class PaymentRouter(
IIntegrationProviderResolver<IPaymentProvider> providers)
{
public IPaymentProvider Resolve(string providerId)
=> providers.GetRequired(providerId);
}

Provider ID 是持久配置值,发布后不得因显示名称或类名变化而修改。

跨 TFM 设计

resolver 使用 scoped 字典 Adapter,不依赖 .NET 8 keyed services:

// net8+ 额外注册 keyed service 作为可选适配器
#if NET8_0_OR_GREATER
services.Add(new ServiceDescriptor(
typeof(TCapability),
descriptor.ProviderId, // 以 ProviderId 作为 key
(serviceProvider, _) => serviceProvider.GetRequiredService<TImplementation>(),
implementationLifetime));
#endif

net5.0 没有 keyed services,但跨 TFM resolver 仍然完全工作。net8+ host 可以选择使用 keyed DI,但 canonical API 是 resolver。

.All 包的验证要求

聚合包必须有测试证明每个配置 Provider 可枚举、可按 ID 解析:

tests/Bitzsoft.Integrations.ProviderRouting.Tests
[Fact]
public void Payment_All_Registers_All_Configured_Providers()
{
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["Payment:Alipay:AppId"] = "test",
["Payment:WeChatPay:AppId"] = "test",
})
.Build();
var services = new ServiceCollection();
services.AddBitzsoftPaymentAll(config);
var sp = services.BuildServiceProvider(validateScopes: true);
var resolver = sp.GetRequiredService<IIntegrationProviderResolver<IPaymentProvider>>();
Assert.True(resolver.TryResolve("Alipay", out _));
Assert.True(resolver.TryResolve("WeChatPay", out _));
}

相关

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%