Core 是连接器库的”事实源”层。它定义 Provider 能力描述、跨 TFM 的目录与解析器、调用上下文、凭据模型和统一异常基类。所有域抽象层都依赖 Core,但 Core 不依赖任何域抽象,也不依赖 .NET 8 keyed services,在 net5.0 上完全工作。本页是 Core 公共 API 的完整参考。
包信息
| 项 | 值 |
|---|---|
| NuGet ID | Bitzsoft.Integrations.Core |
| 目标框架 | net5.0;net8.0;net10.0 |
| 命名空间 | Bitzsoft.Integrations.Core |
| DI 扩展命名空间 | Microsoft.Extensions.DependencyInjection |
类型总览
IntegrationProviderDescriptor
每个 Provider 的可执行能力描述,是 DI 注册与支持矩阵的事实源。该类型 sealed 且不可变,构造完成后所有属性只读。
public sealed class IntegrationProviderDescriptor{ public IntegrationProviderDescriptor( string providerId, string displayName, string domain, IEnumerable<Type> capabilityTypes, IntegrationProviderStability stability, string? vendor = null, string? apiVersion = null, string? authenticationType = null, IEnumerable<string>? regions = null, IEnumerable<string>? environments = null, IEnumerable<string>? limitations = null, DateTimeOffset? lastContractValidatedAt = null);}属性说明:
| 属性 | 类型 | 说明 |
|---|---|---|
ProviderId | string | 稳定、大小写不敏感的标识,发布后不可修改 |
ProviderKey | string | 跨领域唯一键,格式 {Domain}:{ProviderId} |
DisplayName | string | 面向用户的显示名称 |
Domain | string | 所属连接器领域,如 Payment、FileStorage |
Vendor | string? | 厂商名称 |
ApiVersion | string? | 已适配的厂商 API 版本 |
AuthenticationType | string? | 鉴权类型,如 RSA2、OAuth2 |
Stability | IntegrationProviderStability | 支持级别 |
CapabilityTypes | IReadOnlyCollection<Type> | 已实现并会注册到 resolver 的能力类型 |
Regions | IReadOnlyCollection<string> | 适用地区,空集合表示未限定 |
Environments | IReadOnlyCollection<string> | 适用环境,空集合表示未限定 |
Limitations | IReadOnlyCollection<string> | 已知限制 |
LastContractValidatedAt | DateTimeOffset? | 最近一次通过厂商契约验证的时间 |
构造函数会对 providerId、displayName、domain、capabilityTypes 做非空校验,ProviderKey 由 Domain 和 ProviderId 自动拼接。Regions、Environments、Limitations 会被规范化:去空白、去重(大小写不敏感)、排序。
Supports 方法判断描述是否声明了指定能力:
// ① 泛型形式,最常用if (!descriptor.Supports<IPaymentProvider>()) throw new ArgumentException("Descriptor does not declare this capability.");
// ② Type 形式,适合反射场景bool ok = descriptor.Supports(typeof(IPaymentProvider));AddIntegrationProviderCapability 注册时会用 Supports<TCapability>() 校验描述确实声明了该能力,否则抛出 ArgumentException。
Stability 枚举
public enum IntegrationProviderStability{ Experimental = 0, // 仅设计验证或项目级适配,不作生产能力声明 Preview = 1, // 有真实实现与测试,公共契约仍可能调整 Stable = 2, // 已通过契约测试,可作为受支持能力使用 Deprecated = 3 // 仍兼容但不建议新项目使用}Stability 是声明值,由各 Provider 在自己的 Descriptor 中给出,宿主可据此决定是否启用告警或路由策略。
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);}GetRequired 在找不到时抛出 IntegrationProviderResolutionException。Providers 集合按 ProviderKey 去重,注册阶段会拒绝用不同实例重复注册同一 key。
IIntegrationProviderResolver<T>
按大小写不敏感的稳定 Provider ID 解析能力,是跨 TFM 多 Provider 路由的 canonical API。
public interface IIntegrationProviderResolver<TCapability> where TCapability : class{ IReadOnlyCollection<TCapability> Providers { get; } bool TryResolve(string providerId, out TCapability capability); TCapability GetRequired(string providerId);}GetRequired 找不到时抛出 IntegrationProviderResolutionException(继承自 InvalidOperationException),携带 ProviderId 和 CapabilityType。单 Provider 应用可直接注入能力接口,多 Provider 应用必须使用 resolver。
IContextualIntegrationProviderResolver<T>
按显式或环境中的地域、环境条件解析 Provider,用于需要按租户驻留地路由的场景。
public interface IContextualIntegrationProviderResolver<TCapability> where TCapability : class{ IReadOnlyCollection<IntegrationProviderDescriptor> Providers { get; } bool TryResolve(IntegrationProviderSelection selection, out TCapability? provider); TCapability GetRequired(IntegrationProviderSelection selection);}TryResolve 无候选返回 false;多个自动候选时抛出异常,避免静默猜测。GetRequired 在无候选或候选不唯一时抛出明确异常。
IntegrationContext
一次连接器调用的租户、地域和运行环境上下文。sealed 且不可变。
public sealed class IntegrationContext{ public IntegrationContext( string tenantId, string? region = null, string? environment = null, string? correlationId = null);
public string TenantId { get; } public string? Region { get; } public string Environment { get; } // 默认 Production public string? CorrelationId { get; }
public IntegrationContext With( string? region = null, string? environment = null, string? correlationId = null);}With 以不可变方式创建仅替换指定字段的新上下文,原实例不变。Environment 未指定时默认为 IntegrationEnvironments.Production。
IIntegrationContextAccessor
访问当前异步调用链中的连接器上下文。基于 AsyncLocal<T> 实现,跨 await 自动流转。
public interface IIntegrationContextAccessor{ IntegrationContext? Current { get; } IDisposable Push(IntegrationContext context);}Push 在当前异步调用链中压入上下文作用域,必须按后进先出顺序释放返回的 IDisposable:
// ③ 建立短生命周期作用域,using 保证释放using (accessor.Push(new IntegrationContext("tenant-001", region: "CN"))){ await payment.CreateOrderAsync(request);}IntegrationCredential
可释放的 Provider 凭据快照。密钥仅保存在可清零的 char[] 缓冲区中,不通过属性、ToString 或调试显示暴露,Dispose 时清零。
public sealed class IntegrationCredential : IDisposable{ public IntegrationCredential( IEnumerable<KeyValuePair<string, string>> secrets, string name = IntegrationCredentialNames.Default, string? authenticationType = null, DateTimeOffset? expiresAt = null);
public string Name { get; } public string? AuthenticationType { get; } public DateTimeOffset? ExpiresAt { get; } public IReadOnlyCollection<string> SecretNames { get; } // 仅字段名,不含值 public bool IsDisposed { get; }
public bool IsExpiredAt(DateTimeOffset instant); public bool TryGetSecret(string secretName, out string? value); public bool ContainsSecret(string secretName); public string GetRequiredSecret(string secretName); public TResult UseSecret<TResult>(string secretName, Func<ReadOnlyMemory<char>, TResult> callback);}GetRequiredSecret 和 TryGetSecret 会不可避免地创建托管字符串,调用方应缩短其生命周期。UseSecret 在缓冲区有效期间执行回调,避免由本类型主动创建字符串,是签名计算的推荐方式:
// ① UseSecret 在 char[] 有效期内计算 HMAC,不产生中间 stringbyte[] signature = credential.UseSecret("RequestSigningSecret", secret =>{ var key = Encoding.UTF8.GetBytes(secret.ToArray()); using var hmac = new HMACSHA256(key); return hmac.ComputeHash(canonical);});Resolver 返回的实例归调用方所有,使用后应 Dispose(或用 using)。
IIntegrationCredentialStore / IIntegrationCredentialResolver
IIntegrationCredentialStore 是凭据存储抽象,IIntegrationCredentialResolver 是按 IntegrationCredentialKey 解析凭据的接口。生产宿主应优先接入 Vault、KMS 或云密钥服务,并自行注册 IIntegrationCredentialResolver。
InMemoryIntegrationCredentialStore
进程内凭据存储,主要用于开发与测试。AddInMemoryIntegrationCredentials() 同时把它注册为 IIntegrationCredentialStore 和 IIntegrationCredentialResolver 的实现。
var store = new InMemoryIntegrationCredentialStore();store.Store(new IntegrationCredentialKey("tenant-001", "Payment:Alipay"), new IntegrationCredential(new[] { new KeyValuePair<string, string>("AppId", "2021..."), new KeyValuePair<string, string>("PrivateKey", "..."), }, authenticationType: "RSA2"));IntegrationException
所有连接器领域可选用的统一结构化异常基类。宿主无需依赖每个供应商的具体异常即可实施统一错误映射、告警和重试决策。
public class IntegrationException : Exception{ public IntegrationException( string domain, string message, string? provider = null, string? errorCode = null, string? providerMessage = null, IntegrationFailureKind failureKind = IntegrationFailureKind.Provider, int? httpStatusCode = null, string? requestId = null, bool isTransient = false, TimeSpan? retryAfter = null, Exception? innerException = null);}结构化字段:
| 字段 | 说明 |
|---|---|
Domain | 连接器领域,如 Payment、Rest |
Provider | 供应商稳定标识或名称 |
ErrorCode | 供应商或协议错误码 |
ProviderMessage | 供应商返回的原始错误消息,不得默认包含凭据 |
FailureKind | 稳定失败分类 |
HttpStatusCode | 可用时记录的 HTTP 状态码(100–599) |
RequestId | 供应商请求 ID、追踪 ID 或关联 ID |
IsTransient | 是否可在满足幂等性约束后重试 |
RetryAfter | 供应商建议的最短重试等待时间 |
构造函数校验 httpStatusCode 必须在 100–599 之间,retryAfter 不能为负。
IntegrationFailureKind
13 个稳定失败分类,供宿主统一映射状态码、告警与重试策略:
| 值 | 名称 | 含义 |
|---|---|---|
| 0 | Unknown | 无法进一步分类 |
| 1 | Configuration | 配置缺失或配置值无效 |
| 2 | Authentication | 身份认证失败或凭据已失效 |
| 3 | Authorization | 身份有效但没有执行操作的权限 |
| 4 | Validation | 请求参数或业务数据校验失败 |
| 5 | NotFound | 目标资源不存在 |
| 6 | Conflict | 资源状态冲突或重复提交 |
| 7 | RateLimited | 供应商限流 |
| 8 | Network | 网络、DNS、TLS 或连接层失败 |
| 9 | Timeout | 调用超时 |
| 10 | Provider | 供应商返回业务或服务端错误 |
| 11 | Unsupported | 当前 Provider 或版本不支持所请求能力 |
| 12 | Cancelled | 调用被取消 |
IntegrationProviderResolutionException
Provider 注册或解析失败时抛出,继承自 InvalidOperationException:
public sealed class IntegrationProviderResolutionException : InvalidOperationException{ public IntegrationProviderResolutionException( string providerId, Type capabilityType, string message);
public string ProviderId { get; } public Type CapabilityType { get; }}IIntegrationProviderResolver<T>.GetRequired 和 IIntegrationProviderCatalog.GetRequired 在找不到 Provider 时抛出此异常,携带失败的 Provider ID 和能力类型,便于定位。
DI 注册
Core 的 DI 扩展位于 Microsoft.Extensions.DependencyInjection 命名空间。
AddBitzsoftIntegrationCore
注册跨框架 Provider 目录、resolver、contextual resolver、catalog 和 context accessor。所有其他 Core 注册方法内部都会调用它,因此幂等且可安全重复调用。
// ① 注册基础设施:Resolver<>、ContextualResolver<>、Catalog、ContextAccessorservices.AddBitzsoftIntegrationCore();注册的服务生命周期:
| 服务 | 实现 | 生命周期 |
|---|---|---|
IIntegrationProviderResolver<> | IntegrationProviderResolver<> | Scoped |
IContextualIntegrationProviderResolver<> | ContextualIntegrationProviderResolver<> | Scoped |
IIntegrationProviderCatalog | IntegrationProviderCatalog | Scoped |
IIntegrationContextAccessor | IntegrationContextAccessor | Singleton |
AddInMemoryIntegrationCredentials
注册进程内凭据存储。内部先调用 AddBitzsoftIntegrationCore,再把 InMemoryIntegrationCredentialStore 同时绑定为 IIntegrationCredentialStore 和 IIntegrationCredentialResolver。
services.AddInMemoryIntegrationCredentials();AddIntegrationProviderDescriptor
将 Provider 描述加入目录,即使实验性 Provider 暂未暴露任何能力也可被发现。按 ProviderKey 去重:同一 key 用相同实例重复注册是幂等的,用不同实例则抛出 ArgumentException。
services.AddIntegrationProviderDescriptor(AlipayProviderDescriptor.Instance);AddIntegrationProviderCapability
注册 Provider 的一个真实能力。这是连接器库最核心的注册方法:
// ② 类型形式services.AddIntegrationProviderCapability<IPaymentProvider, AlipayPaymentProvider>( AlipayProviderDescriptor.Instance, implementationLifetime: ServiceLifetime.Transient, registerLegacyAlias: true);
// ③ 工厂形式(Provider 构造函数含 internal Adapter 时必须用)services.AddIntegrationProviderCapability<IPaymentProvider, AlipayPaymentProvider>( AlipayProviderDescriptor.Instance, serviceProvider => new AlipayPaymentProvider( serviceProvider.GetRequiredService<AlipayHttpClient>(), serviceProvider.GetRequiredService<IAlipayConfigProvider>(), serviceProvider.GetRequiredService<ILogger<AlipayPaymentProvider>>()));注册内部完成四件事:
- 用
Supports<TCapability>()校验描述声明了该能力; - 提交 Descriptor 到目录(按
ProviderKey去重); TryAdd实现类型 + 添加IntegrationProviderRegistration<T>单例;- net8+ 额外注册 keyed service(以
ProviderId为 key),net5 跳过。
registerLegacyAlias = true(默认)会用 TryAdd 注册直接能力接口别名,兼容单 Provider 应用直接注入。多 Provider 应用不应依赖此别名,必须用 resolver。
消费模式
单 Provider 直接注入
public sealed class CheckoutService(IPaymentProvider payment){ public Task<PaymentResult<PaymentOrderResult>> PayAsync(PaymentOrderRequest req) => payment.CreateOrderAsync(req);}多 Provider 路由
public sealed class PaymentRouter(IIntegrationProviderResolver<IPaymentProvider> providers){ public IPaymentProvider Resolve(string providerId) => providers.GetRequired(providerId); // ④ 按稳定 ID 解析}