Aore.ApiKit 1.0.0

dotnet add package Aore.ApiKit --version 1.0.0
                    
NuGet\Install-Package Aore.ApiKit -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Aore.ApiKit" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Aore.ApiKit" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Aore.ApiKit" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Aore.ApiKit --version 1.0.0
                    
#r "nuget: Aore.ApiKit, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Aore.ApiKit@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Aore.ApiKit&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Aore.ApiKit&version=1.0.0
                    
Install as a Cake Tool

Aore.ApiKit

生产环境可直接使用的 .NET 外部接口调用核心库:用统一的链式 API 快速调用外部 HTTP/HTTPS 接口与 SOAP WebService(动态构建协议 XML),内建重试、超时、调用日志(可插拔)、HttpClient 连接复用、XML 安全解析(拒绝 DTD/外部实体)、SSRF 防护开关与凭证脱敏。

版本 1.0.0 | 唯一外部依赖:Newtonsoft.Json 13.0.3 目标框架:netstandard2.0 · net462 · net472 · net48 · net8.0 · net9.0 · net10.0(单包多目标,条件编译消化平台差异)

特性总览

  • 链式请求:动词、请求头、查询参数、认证、超时、重试一行搞定(Flurl 风格体验,独立实现)
  • 双模式错误处理:异常模式(快速失败)/ Result 模式(Try* 不抛异常,逐调用判定)
  • 动词 × 正文格式全覆盖:JSON / XML / 表单 / 文本 / multipart,各动词均有专用便捷方法
  • 认证全家桶:Basic / Bearer / API Key / Cookie / Windows 集成认证 / OAuth2 客户端凭证(令牌缓存 + 并发单飞)/ 客户端证书(mTLS)
  • 重试策略:固定/线性/指数退避 + 抖动 + Retry-After 遵循 + 状态码白名单 + OnRetry 回调
  • 超时体系:单次尝试超时(每次重试独立计时)+ 总调用超时(覆盖全部尝试与响应体读取);调用方取消与库超时严格区分
  • 调用日志:每次调用记录请求参数与响应数据;委托 / IAoreCallLogger 接口 / 内置 Sink(Console/File 按日滚动/Trace/Composite)三种接入;自动脱敏与截断;日志异常永不影响业务调用
  • SOAP WebService:SOAP 1.1/1.2 动态报文构建(XmlWriter)、SOAPAction 推导、WS-Security UsernameToken(Digest/Text)、WS-Addressing、Fault 统一解析、?WSDL 自动命名空间、DataSet/DataTable 兼容
  • 请求模板:AoreApiKit.Template(baseUrl)——一个服务一个模板,复用协议/IP/头/认证/超时,每个 API 只传路径与参数(MES/ERP 场景)
  • 三级设置继承:全局 → 命名客户端 → 请求,逐级覆盖(写时复制,纯读取请求零额外深拷贝)
  • 同步方法:全 API 同步/异步双形态(同步上下文安全,防死锁)
  • 安全内建:scheme 白名单恒开(仅 http/https)、XML 安全解析(拒绝 DOCTYPE/ENTITY/外部实体,不可关闭)、SSRF 私网阻断开关、JSON/XML/表单凭证脱敏
  • 质量:0 警告 0 错误构建(TreatWarningsAsErrors + 全量分析器)、net8.0 + net48 双平台 250+ 项测试全绿(含真实服务端到端实测)

安装

dotnet add package Aore.ApiKit
<PackageReference Include="Aore.ApiKit" Version="1.0.0" />

常用 using(重要):

using Aore.ApiKit;              // AoreApiKit / AoreKitRequest / AoreResponse / 扩展方法
using Aore.ApiKit.Soap;         // SoapCaller / SoapParam / SoapVersion / AsDataTable
using Aore.ApiKit.Logging;      // IAoreCallLogger / AoreCallLoggers / AoreCallLog
using Aore.ApiKit.Exceptions;   // 异常体系
using Aore.ApiKit.Auth;         // OAuth2ClientCredentials 等认证扩展

30 秒快速上手

using Aore.ApiKit;

// HTTP:GET + JSON 反序列化(非 2xx 抛 AoreHttpException)
var user = await "https://api.example.com/users/42".GetJsonAsync<User>();

// HTTP:POST JSON
await "https://api.example.com/users".PostJsonAsync(new { name = "tom" });
using Aore.ApiKit.Soap;

// SOAP:动态调用 WebService
var soap = new SoapCaller("http://esb.example.com/OrderService.asmx")
    .WithNamespace("http://tempuri.org/");
var r = await soap.CallAsync("GetOrder", new SoapParam("orderNo", "SO2026"));
Console.WriteLine(r.Value("Status"));

HTTP 调用

四种调用入口

// 1) 字符串快捷入口(一行调用)
var dto = await "https://api.example.com/users/42".GetJsonAsync<User>();

// 2) 门面入口(推荐:链式配置后发送)
var resp = await AoreApiKit.Request("https://api.example.com/users")
    .SetQueryParam("page", 2)
    .WithHeader("X-Trace-Id", traceId)
    .GetAsync();

// 3) 命名客户端(连接复用 + 客户端级设置)
var resp2 = await AoreApiKit.Request(url, "mes-service").GetAsync();

// 4) 请求模板(一个服务一个模板,见下文)
var resp3 = await AoreApiKit.Template("http://mes:8080/api/v1").Request("/users/1").GetAsync();

动词 × 参数格式矩阵

动词 \ 格式 URL 查询串 JSON body XML body 表单 文本 multipart
GET SetQueryParam ✅ SendAsync(Get, content)(net8+;netfx 平台限制) 同左 — 同左 同左
POST ✅ PostJsonAsync[<T>] PostXmlAsync PostFormAsync PostStringAsync PostMultipartAsync[<T>]
PUT ✅ PutJsonAsync[<T>] PutXmlAsync SendAsync(Put, …) 同左 同左
PATCH ✅ PatchJsonAsync[<T>] PatchXmlAsync SendAsync(Patch, …) 同左 同左
DELETE ✅ DeleteJsonAsync[<T>] SendAsync(Delete, …) 同左 同左 同左
HEAD / OPTIONS ✅ — — — — —

约定(DR-010):GetAsync 等"返回响应"的方法不因状态码抛异常;GetJsonAsync 等"返回数据"的方法非 2xx 抛 AoreHttpException。矩阵外的组合用万能方法 SendAsync(method, content) / SendAsync(method, () => content工厂, logBody)。

// 读取 201 响应体
var created = await AoreApiKit.Request(url).PostJsonAsync<CreateResult>(new { name = "tom" });

// DELETE 携带 JSON 条件("带条件删除"类接口)
var del = await AoreApiKit.Request(url + "/42").DeleteJsonAsync<DeleteResult>(new { reason = "dup" });

// 表单登录
await AoreApiKit.Request(url + "/login").PostFormAsync(new { username = "tom", password = pwd });

// 原生内容(工厂重载重试安全;第二个参数为日志正文)
var resp = await AoreApiKit.Request(url).SendAsync(
    AoreHttpMethod.Post,
    () => new StringContent(jsonText, Encoding.UTF8, "application/json"),
    jsonText,
    cancellationToken);

URL 构建(路径段与查询参数自动转义)

var request = AoreApiKit.Request("https://api.example.com")
    .AppendPathSegment("users")                          // 中文/空格/斜杠自动转义;已整体预编码的片段原样保留
    .SetQueryParam("page", 2)                            // 标量(InvariantCulture)
    .SetQueryParam("tag", "a b&c")                       // 特殊字符自动转义
    .SetQueryParams(new { source = "app", debug = true }) // 匿名对象/字典批量
    .SetQueryParam("draft", null);                       // value=null 移除该参数

Console.WriteLine(request.Url);                          // https://api.example.com/users?page=2&tag=a%20b%26c&source=app&debug=True
request.Url.GetQueryParam("page");                       // "2"(取值自动解码)

响应处理(AoreResponse)

var response = await AoreApiKit.Request(url).GetAsync();
using (response)                                          // 实现 IDisposable
{
    (int)response.StatusCode;                             // 状态码
    response.IsSuccessStatusCode;                         // 含 AllowHttpStatus 白名单扩展
    response.Headers["Content-Type"];                     // 响应头+内容头合并视图
    response.ElapsedMs;                                   // 总耗时(毫秒,含重试)
    response.CallId;                                      // 调用唯一标识(跨重试不变,与日志对应)

    await response.GetStringAsync();                      // 文本
    await response.GetBytesAsync();                       // 字节
    await response.GetStreamAsync();                      // 流(大响应不整包缓冲)
    await response.ReadJson<User>();                      // JSON 反序列化(失败抛 AoreParsingException)
    await response.ReadXml();                             // 安全 XML(拒绝 DTD/实体)

    response.EnsureSuccessStatusCode();                   // 非 2xx 抛 AoreHttpException(消息含响应体预览)
}

Result 模式与异常体系

var result = await "https://api.example.com/users/42".TryGetJsonAsync<User>();
if (result.IsSuccess)
{
    var user = result.Value;          // 反序列化数据
}
else
{
    var status = (result.Error as AoreHttpException)?.StatusCode;  // 分类处理
    result.Response?.Dispose();       // HTTP 失败时 Response 保持未释放,由调用方处置
}
// 同族:TrySendAsync / TryPostJsonAsync<T>(及同步版 TrySend / TryGetJson / TryPostJson)
异常类型 触发场景
AoreUrlException URL 非法 / scheme 不支持(构造期快速失败)
AoreSecurityException 安全策略(SSRF 私网阻断、XML 含 DTD)
AoreTimeoutException 请求超时(含单次尝试与总超时)
AoreTransportException 网络传输失败(连接拒绝/DNS/中断)
AoreHttpException 响应状态码非成功(含响应体预览)
AoreParsingException JSON/XML 解析失败(含 RawPreview 原文预览)

均继承 AoreApiKitException,统一携带 CallId(与调用日志对应);SOAP Fault 另有 SoapFaultException(携带解析后的 Fault)。

认证

// Basic(自动 Base64)
request.WithBasicAuth("tom", "s3cret");

// Bearer 令牌
request.WithBearerToken("eyJ...");

// API Key(自定义头)
request.WithApiKey("X-Api-Key", "key-1");

// Cookie(多枚自动合并为一个 Cookie 头)
request.WithCookie("session", "abc").WithCookie("locale", "zh-CN");

// Windows 集成认证(内网 IIS + Negotiate)
request.WithCredentials(CredentialCache.DefaultCredentials);

// OAuth2 客户端凭证模式(provider 创建一次跨请求复用;令牌内存缓存、到期前刷新、并发单飞)
var provider = new OAuth2ClientCredentials(
    tokenEndpoint, clientId, clientSecret,
    scopes: "read write",
    authStyle: OAuthTokenAuthStyle.RequestBody);      // 或 BasicHeader(凭证放 Basic 头)
request.WithOAuth2(provider);                         // 每次请求自动附加 Bearer
// 一次性用法:request.WithOAuth2ClientCredentials(endpoint, clientId, clientSecret);

// 双向 TLS / 自签名(net48/net8+;高风险开关仅限测试环境)
request.WithClientCertificate(new X509Certificate2(pfxPath, pwdFromEnv));
request.WithInsecureSkipTlsVerify();

超时、重试与取消

var response = await AoreApiKit.Request(url)
    .WithTimeout(TimeSpan.FromSeconds(10))             // 单次尝试超时(每次重试独立计时)
    .WithTotalTimeout(TimeSpan.FromSeconds(30))        // 总调用超时:覆盖校验/钩子/全部尝试/重试等待/响应体读取
    .WithRetry(3, RetryBackoff.Exponential, policy =>
    {
        policy.BaseDelay = TimeSpan.FromMilliseconds(200);
        policy.MaxDelay = TimeSpan.FromSeconds(5);
        policy.Jitter = 0.2;                           // ±20% 抖动
        policy.RetryOnTimeout = false;                 // 超时重试需显式开启(幂等性由调用方决策)
        policy.OnRetry = (attempt, delay, reason) => log(attempt, delay, reason);
    })
    .GetAsync();

// 重试白名单默认 408/429/500/502/503/504;遵循服务端 Retry-After 头
// 状态码白名单:把额外状态码视为成功
request.AllowHttpStatus(404, 409);

// 调用方取消(与库超时严格区分:取消抛 OperationCanceledException,超时抛 AoreTimeoutException)
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await AoreApiKit.Request(url).GetJsonAsync<User>(cts.Token);

生产建议:每次业务调用设置 WithTotalTimeout 总超时兜底——未设置时单次尝试超时约束到响应头与库内采集,调用方后续的流式读取遵循其传入的取消令牌。

调用日志(记录请求参数与响应数据)

// 方式一:委托(最轻接入;每次调用多条日志,重试时每次尝试各一条,CallId 跨重试不变)
request.WithCallLogger(entry =>
{
    entry.Tags["OrderId"] = orderId;                   // 附加业务标识
    logger.Info(entry.Summary());                      // 单行摘要(时间/阶段/URL/状态/耗时)
});

// 方式二:内置 Sink
AoreApiKit.Settings.Logger = AoreCallLoggers.File(@"logs\api-call.log"); // 按日滚动 api-yyyyMMdd.log
AoreApiKit.Settings.Logger = AoreCallLoggers.Console(AoreCallLogFormat.Json); // 结构化单行
AoreApiKit.Settings.Logger = AoreCallLoggers.Composite(console, fileLogger);  // 组合分发

// 方式三:实现 IAoreCallLogger 对接任意日志平台(库零日志框架依赖,不绑定 Serilog/NLog/MEL)
public sealed class MyCallLogger : IAoreCallLogger
{
    public void Log(AoreCallLog entry) => myLogger.Info(entry.Summary());
}
request.WithCallLogger(new MyCallLogger());

日志自动脱敏与截断(可通过 AoreSettings.LogRedaction 配置):

  • 请求/响应头:Authorization / Cookie / Set-Cookie / Proxy-Authorization
  • XML/SOAP 元素:Password / Passwd / Nonce / Token / Secret
  • JSON 键值 / 表单键值:password / token / api_key / access_token / client_secret 等
  • 正文超过 MaxLoggedBodyLength(默认 4096 字符)自动截断并标注;日志器抛出的异常被库隔离,绝不影响业务调用

请求模板(一个服务一个模板)

典型场景:调用 MES/ERP 等固定服务——协议、IP、端口、请求头、认证、超时都一样,仅 URL 与参数不同。

// 服务模板:配置一次(并发安全),整组 API 复用
var mes = AoreApiKit.Template("http://mes:8080/api/v1")
    .WithHeader("X-App", "mes-gateway")
    .WithBasicAuth("svc", "pwd")                       // 也支持 Bearer/ApiKey/Windows/OAuth2
    .WithTimeout(TimeSpan.FromSeconds(10))
    .WithTotalTimeout(TimeSpan.FromSeconds(30))
    .WithRetry(2)
    .WithCallLogger(entry => auditLog.Write(entry.Summary()));

// 每个 API 只传不同路径与参数
var u1 = await mes.Request("/users/1").GetJsonAsync<User>();
var u2 = await mes.Request("/users/2").GetJsonAsync<User>();

// 请求级仍可覆盖模板级(仅覆盖本次请求);传绝对地址则整体覆盖模板地址
var special = await mes.Request("/users/3").WithTimeout(TimeSpan.FromSeconds(3)).GetAsync();

SOAP WebService

var soap = new SoapCaller("http://esb.example.com/OrderService.asmx")
    .WithNamespace("http://tempuri.org/")              // 已知命名空间推荐显式指定;未知用 WithAutoNamespace()(自动取 ?WSDL 并缓存)
    .WithWsSecurity(ws => ws                           // WS-Security UsernameToken(默认 Digest;Text 依赖 HTTPS 保护)
        .Username("svc-user")
        .Password("svc-pass", WsPasswordType.Digest))
    .WithRetry(2)                                      // SOAP 与 HTTP 共用同一弹性管线
    .WithCallLogger(entry => auditLog.Write(entry.Summary())); // Category=SOAP

// 参数类型全集:标量(InvariantCulture)/日期/decimal/复杂对象/集合/字典/XElement/nil
var response = await soap.CallAsync(
    "GetOrders",
    new SoapParam("beginDate", new DateTime(2026, 1, 1)),
    new SoapParam("minAmount", 199.99m),
    new SoapParam("pager", new { PageIndex = 1, PageSize = 20 }),  // 复杂对象 → 子元素
    new SoapParam("statuses", new[] { "NEW", "PAID" }),            // 集合 → 同名重复元素
    new SoapParam("filter", new Dictionary<string, object?> { ["warehouse"] = "WH01" }));

if (response.HasFault)
{
    Console.WriteLine(response.Fault);                 // Code/Reason/Actor/Node/Detail 统一解析(1.1 与 1.2)
}
else
{
    response.Value("Status");                          // 值访问器(不区分命名空间与大小写)
    response.Value<decimal>("Total");                  // 强类型取值(InvariantCulture)
    var orders = response.ReadXml<OrderListDto>("GetOrdersResult"); // XmlSerializer 强类型映射
    var table  = response.Result!.AsDataTable();       // DataTable/DataSet 兼容(net462-48/net8+,绑定 DataGridView)
    response.Dispose();                                // 读完释放底层 HTTP 响应
}

// 其余能力:WithSoapVersion(SoapVersion.V12) / WithSoapHeader(自定义头) / WithAddressing(WS-Addressing) /
//           SendRawAsync(原始信封) / soap.Call(同步) / SOAPAction 显式覆盖 WithSoapAction

SOAP 参数类型映射:标量按 xsd 规范(bool→true/false、DateTime→xsd:dateTime 带时区、byte[]→Base64);匿名对象/实体类按公共可读属性递归;字典键为子元素名;集合同名重复;XElement 原样写入;null → xsi:nil;DataSet/DataTable/IXmlSerializable/带 XmlRoot·XmlType 标注类型 走 XmlSerializer 兜底。参数值全部经 XmlWriter 写入(自动转义),不存在 XML 注入。

同步 API

全 API 同步/异步双形态;同步版内部做了同步上下文安全调度(WinForms/WPF/ASP.NET 经典/控制台均可直接调用)。请勿自行用 .Result / .Wait() 包装本库异步 API,直接用同步版。

var response = AoreApiKit.Request(url).Get();                       // 不断言
var dto      = AoreApiKit.Request(url).GetJson<User>();             // 断言 + 反序列化
var created  = AoreApiKit.Request(url).PostJson<User>(new { name = "tom" });
var result   = AoreApiKit.Request(url).TryGetJson<User>();          // Result 模式(不抛异常)
var r        = soap.Call("GetOrders", new SoapParam("id", 1));      // SOAP 同步

高级用法

三级设置继承与命名客户端

// 全局(进程级基线)
AoreApiKit.Settings.Timeout = TimeSpan.FromSeconds(15);

// 命名客户端(首次注册生效;同名的所有请求共享连接池,避免套接字耗尽)
AoreApiKit.Clients.GetOrAdd("mes", settings =>
{
    settings.Timeout = TimeSpan.FromSeconds(10);
    settings.Retry.MaxAttempts = 3;
    settings.DefaultHeaders["X-From-Client"] = "gateway";
});
var resp = await AoreApiKit.Request(url, "mes").GetAsync();

// 请求级覆盖(WithClient 切换后已记录的请求级变更自动重放)
await AoreApiKit.Request(url, "mes").WithTimeout(TimeSpan.FromSeconds(5)).GetAsync();

钩子(发送前/响应后)

await AoreApiKit.Request(url)
    .OnBeforeRequest((context, ct) =>       // 每次尝试都会执行(重试也生效);可附加动态签名/令牌头
    {
        context.AddHeader("X-Signature", Sign(context.CallId));
        return Task.CompletedTask;
    })
    .OnAfterResponse((context, resp, ct) => // 埋点/熔断统计;抛异常会中断调用
    {
        metrics.Record(resp.StatusCode, resp.ElapsedMs);
        return Task.CompletedTask;
    })
    .GetAsync();

上传与下载

// multipart 上传:文本/JSON 字段 + byte[]/流文件(流式文件重试需可 Seek;稳妥用 byte[])
await AoreApiKit.Request(url).PostMultipartAsync(b =>
{
    b.AddString("note", "说明");
    b.AddJson("meta", new { owner = "tom" });
    b.AddFile("file", "a.txt", fileBytes, "text/plain");
});

// 流式下载:边下边处理不整包缓冲;释放返回的流即释放底层响应
using (var stream = await AoreApiKit.Request(url).GetStreamAsync())
{
    await stream.CopyToAsync(File.Create("report.bin"));
}

自定义 HttpClient / 接入 Polly

// 调用方自备 HttpClient(设置仍由本库管理)
request.UsingHttpClient(myHttpClient);

// handler 工厂接入第三方 DelegatingHandler(如 Polly)
var client = new AoreKitClient("polly", settings, () => new PolicyHandler(new HttpClientHandler()));

安全内建

能力 说明
scheme 白名单 仅 http/https,恒开不可关(ftp://、file:// 构造期即拒绝)
XML 安全解析 SafeXml:DtdProcessing=Prohibit + XmlResolver=null + 8M 字符上限,不可关闭,杜绝 XXE
SSRF 私网阻断 settings.Security.BlockPrivateNetworks = true(默认关闭):环回/私有/链路本地/保留地址(IPv4+IPv6 内置网段表)发送前校验;开启后自动禁用重定向
凭证脱敏 日志与异常消息均无 Authorization/Cookie 明文;JSON/XML/表单正文凭证字段掩码
TLS net462-48 启动时调用 AoreApiKit.EnsureTls12();InsecureSkipTlsVerify 为显式高风险开关(日志标记 insecure_tls)

已知限制:SSRF 校验与实际建连存在两次 DNS 解析,短 TTL 域名理论上存在 DNS rebinding 窗口,严格场景请用 IP 直连。

生产注意事项

  • 总超时兜底:建议每次业务调用 .WithTotalTimeout(...)(见"超时、重试与取消")
  • 内网地址:SSRF 阻断默认关闭(内网 ESB/WebService 是常态场景);面向不可信输入的服务端应用请开启
  • net462-48:启动时调用 AoreApiKit.EnsureTls12();注意 System.Net.Http 绑定重定向
  • netfx GET 携带 body:平台限制会统一包装为 AoreTransportException,此类服务建议改 POST
  • WSS 摘要:按 OASIS 规范使用 SHA1(互操作要求,不可更换)
  • multipart 流式上传:重试要求流可 Seek(不可 Seek 流在重试时显式失败);更稳妥用 AddFile(name, fileName, byte[])
  • handler 级设置(TLS 校验/客户端证书/代理/Cookie/凭证/重定向/解压/连接超时)建议直接用命名客户端;请求级覆盖会按指纹缓存专用客户端(证书按 Thumbprint 精确区分)
  • 高频调用:日志 Sink 建议自定义异步缓冲实现(内置 File Sink 为锁内写入、常驻 StreamWriter、按日滚动)
  • AOT/裁剪:当前基于 Newtonsoft 反射序列化,未声明 NativeAOT 支持

许可证

MIT · Copyright © 2026 WEI.ZHOU (Willis). All rights reserved.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 is compatible.  net48 is compatible.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 86 9/26/2026