ImageServer.SDK
0.6.0
dotnet add package ImageServer.SDK --version 0.6.0
NuGet\Install-Package ImageServer.SDK -Version 0.6.0
<PackageReference Include="ImageServer.SDK" Version="0.6.0" />
<PackageVersion Include="ImageServer.SDK" Version="0.6.0" />
<PackageReference Include="ImageServer.SDK" />
paket add ImageServer.SDK --version 0.6.0
#r "nuget: ImageServer.SDK, 0.6.0"
#:package ImageServer.SDK@0.6.0
#addin nuget:?package=ImageServer.SDK&version=0.6.0
#tool nuget:?package=ImageServer.SDK&version=0.6.0
ImageServer SDK
C# 客户端 SDK for ImageServer V1 & V2 APIs
📋 概述
ImageServer SDK 是一个功能完整的 C# 客户端库,用于与 KayiImageServer V1 和 V2 API 进行交互。SDK 采用模块化设计,支持依赖注入和直接实例化两种使用模式,适用于 ASP.NET Core、控制台应用、WPF、WinForms 等各种 .NET 应用场景。
核心特性
✅ 单一 NuGet 包 - 一个包同时包含 V1 和 V2 客户端 ✅ 共享基础设施 - 认证、租户管理、用户信息等功能在 V1 和 V2 中完全复用 ✅ 双模式支持 - 同时支持依赖注入 (DI) 和非依赖注入 (直接实例化) ✅ HttpClient 优化 - DI 模式使用 IHttpClientFactory,非 DI 模式使用连接池 ✅ 多租户支持 - 内置租户上下文管理和动态切换功能 ✅ 并发操作 - 支持多客户端实例并发访问 ✅ 完善的日志 - 集成 Microsoft.Extensions.Logging 抽象 ✅ 类型安全 - 完整的 DTO 模型定义,支持强类型编程
🚀 快速开始
安装
dotnet add package ImageServer.SDK
非 DI 模式 (控制台应用/桌面应用)
using ImageServer.SDK.V1;
using ImageServer.SDK.Core.Configuration;
using ImageServer.SDK.Core.Authentications.Models;
// 配置客户端
var config = new ImageServerClientConfig
{
BaseUrl = "https://imageserver.hospital.com",
Timeout = TimeSpan.FromSeconds(30)
};
// 创建 V1 客户端
var client = new ImageServerV1Client(config);
// 登录
var loginRequest = new LoginRequest
{
Username = "admin",
Password = "your-password"
};
var loginResult = await client.Authentication.LoginAsync(loginRequest);
// 设置访问令牌
client.SetAccessToken(loginResult.AccessToken);
// 获取所有租户
var tenants = await client.Tenant.GetAllTenantsAsync();
Console.WriteLine($"找到 {tenants.Count} 个租户");
// 设置租户上下文
client.SetTenantId(tenants[0].Id.ToString());
// 获取查询引擎
var engines = await client.QueryEngine.GetAllQueryEnginesAsync();
foreach (var engine in engines)
{
Console.WriteLine($"查询引擎: {engine.Title}");
}
DI 模式 (ASP.NET Core 推荐)
// Program.cs 或 Startup.cs
builder.Services.AddImageServerV1Client(options =>
{
options.BaseUrl = "https://imageserver.hospital.com";
options.Timeout = TimeSpan.FromSeconds(30);
});
// 或使用配置文件
builder.Services.AddImageServerV1Client(
builder.Configuration.GetSection("ImageServer:V1"));
// Controller
public class TenantController : ControllerBase
{
private readonly IImageServerV1Client _client;
public TenantController(IImageServerV1Client client)
{
_client = client;
}
[HttpGet("tenants")]
public async Task<IActionResult> GetTenants()
{
var tenants = await _client.Tenant.GetAllTenantsAsync();
return Ok(tenants);
}
}
📚 文档导航
核心文档
- SDK 使用指南 (SDK_USAGE_GUIDE.md) - 完整的 API 使用文档,包含所有接口方法签名和数据结构
- CLAUDE.md - Claude Code 使用指南,项目架构和开发规范
API 参考
🏗️ 项目架构
单一 NuGet 包,多项目解决方案
ImageServer.SDK (NuGet 包)
├── ImageServer.SDK.Core.dll # 核心共享功能
│ ├── Authentication # ✅ 身份认证 (V1 & V2 共享)
│ ├── Tenancy # ✅ 租户管理 (V1 & V2 共享)
│ ├── UserInfo # ✅ 用户信息 (V1 & V2 共享)
│ ├── Http Client Management # ✅ HTTP 客户端管理
│ ├── Context Management # ✅ 上下文管理 (访问令牌、租户 ID)
│ └── Models # ✅ 通用数据模型
├── ImageServer.SDK.V1.dll # V1 专属功能
│ ├── QueryEngines # ❌ 查询引擎管理 (V1 独有)
│ └── QueryEngineProxies # ❌ 查询引擎代理 (V1 独有)
├── ImageServer.SDK.V2.dll # V2 专属功能
│ └── Endpoints # ❌ 端点管理 (V2 独有)
│ ├── BaseInfoEndpoints # - 基础信息端点
│ ├── ImageQueryEndpoints # - 图像查询端点
│ ├── PacsEndpoints # - PACS 端点
│ └── StudyQueryEndpoints # - 检查查询端点
└── ImageServer.SDK.dll # 统一入口点
└── DI Extensions # 依赖注入扩展方法
功能对比表
| 功能模块 | V1 客户端 | V2 客户端 | 说明 |
|---|---|---|---|
| 身份认证 | ✅ | ✅ | OAuth2 密码授权,共享实现 |
| 租户管理 | ✅ | ✅ | 完整的 CRUD 操作,共享实现 |
| 用户信息 | ✅ | ✅ | 获取当前用户资料,共享实现 |
| 查询引擎 | ✅ | ❌ | V1 专属:管理查询引擎配置 |
| 查询引擎代理 | ✅ | ❌ | V1 专属:1 个引擎对应 N 个代理 |
| BaseInfo 端点 | ❌ | ✅ | V2 专属:基础信息端点管理 |
| ImageQuery 端点 | ❌ | ✅ | V2 专属:图像查询端点管理 |
| Pacs 端点 | ❌ | ✅ | V2 专属:PACS 端点管理 |
| StudyQuery 端点 | ❌ | ✅ | V2 专属:检查查询端点管理 |
🔧 高级功能
多租户上下文管理
// 方式 1: 使用作用域 (推荐,自动清理)
using (client.UseTenantScope(tenantId))
{
// 在此范围内的所有请求都使用指定的 tenantId
var engines = await client.QueryEngine.GetAllQueryEnginesAsync();
}
// 离开作用域后,租户 ID 自动恢复
// 方式 2: 手动设置 (全局影响)
client.SetTenantId(tenantId);
var data = await client.QueryEngine.GetAllQueryEnginesAsync();
// 方式 3: Host 模式 (用于租户管理操作)
client.SetTenantId(null); // 清除租户 ID
var tenants = await client.Tenant.GetAllTenantsAsync();
多客户端并发访问
// 非 DI 模式 - 创建多个客户端实例
var config1 = new ImageServerClientConfig { BaseUrl = "https://hospital1.com" };
var config2 = new ImageServerClientConfig { BaseUrl = "https://hospital2.com" };
var client1 = new ImageServerV1Client(config1);
var client2 = new ImageServerV1Client(config2);
// 并发访问不同服务器
var task1 = client1.Tenant.GetAllTenantsAsync();
var task2 = client2.Tenant.GetAllTenantsAsync();
await Task.WhenAll(task1, task2);
// DI 模式 - 命名客户端
services.AddImageServerV1Client("Hospital1", options =>
{
options.BaseUrl = "https://hospital1.com";
});
services.AddImageServerV1Client("Hospital2", options =>
{
options.BaseUrl = "https://hospital2.com";
});
// 使用命名客户端管理器
public class MyService
{
private readonly INamedImageServerClientManager _clientManager;
public MyService(INamedImageServerClientManager clientManager)
{
_clientManager = clientManager;
}
public async Task ProcessAsync()
{
var client1 = _clientManager.GetV1Client("Hospital1");
var client2 = _clientManager.GetV1Client("Hospital2");
// 并发操作
await Task.WhenAll(
client1.Tenant.GetAllTenantsAsync(),
client2.Tenant.GetAllTenantsAsync()
);
}
}
自定义日志记录
// 非 DI 模式 - 传入自定义 ILogger
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
builder.AddDebug();
builder.SetMinimumLevel(LogLevel.Information);
});
var logger = loggerFactory.CreateLogger<ImageServerV1Client>();
var client = new ImageServerV1Client(config, logger);
// DI 模式 - 自动注入
services.AddLogging(builder =>
{
builder.AddConsole();
builder.AddDebug();
});
services.AddImageServerV1Client(options => { });
// ILogger<T> 会自动注入
HttpClient 缓存和连接池
SDK 自动管理 HttpClient 生命周期:
- DI 模式: 使用
IHttpClientFactory,自动处理连接池 - 非 DI 模式: 使用内置
HttpClientManager,基于配置哈希缓存 HttpClient 实例
// HttpClient 会根据以下配置进行缓存:
// - BaseUrl
// - Timeout
// - 默认请求头
// 相同配置复用同一个 HttpClient 实例,避免套接字耗尽
📊 开发状态
| 阶段 | 状态 | 进度 |
|---|---|---|
| 📝 需求分析 | ✅ 已完成 | 100% |
| 📐 架构设计 | ✅ 已完成 | 100% |
| 🏗️ 核心基础设施 | ✅ 已完成 | 100% |
| 🔧 V1 SDK 实现 | ✅ 已完成 | 100% |
| 🔧 V2 SDK 实现 | ✅ 已完成 | 100% |
| 🔌 DI 扩展实现 | ✅ 已完成 | 100% |
| 📖 使用文档 | ✅ 已完成 | 100% |
| 🧪 单元测试 | 🚧 进行中 | 20% |
| 🧪 集成测试 | ⏸️ 未开始 | 0% |
| 📦 NuGet 打包 | ⏸️ 未开始 | 0% |
当前版本: v1.0.0-beta 最后更新: 2025-11-04
🎯 使用场景
适用平台
- ✅ ASP.NET Core Web API
- ✅ Blazor Server / WebAssembly
- ✅ 控制台应用
- ✅ WPF 桌面应用
- ✅ WinForms 应用
- ✅ 后台服务 (IHostedService)
- ✅ Azure Functions / AWS Lambda
- ✅ .NET Framework 4.6.2+ 应用
典型应用场景
- 医疗影像系统集成 - 连接多个医院的 ImageServer,实现跨院影像共享
- PACS 系统对接 - 管理 PACS 端点和查询引擎配置
- 多租户 SaaS 平台 - 租户隔离和动态切换,支持多医院部署
- 数据同步服务 - 后台定时同步租户数据和影像信息
- 监控和告警系统 - 监控 ImageServer 健康状态和性能指标
- 影像检索服务 - 基于查询引擎实现高级影像检索功能
🛠️ 技术栈
目标框架
- .NET Standard 2.0 - 兼容 .NET Framework 4.6.2+ 和 .NET Core 2.0+
- .NET Framework 4.6.2 - 完整的 .NET Framework 支持
- .NET 9.0 - 最新的 .NET 支持,性能优化
核心依赖
- System.Net.Http - HTTP 客户端
- System.Text.Json (9.0.10) - JSON 序列化
- Microsoft.Extensions.Logging.Abstractions (9.0.10) - 日志抽象
- Microsoft.Extensions.DependencyInjection.Abstractions (9.0.10) - DI 抽象
- Microsoft.Extensions.Http (9.0.10) - IHttpClientFactory
- Microsoft.Extensions.Options (9.0.10) - 配置选项模式
- Microsoft.Extensions.Caching.Memory (9.0.10) - 内存缓存
- Polly (8.6.4) - 弹性和瞬态故障处理
📖 完整示例
V1 客户端完整工作流
using ImageServer.SDK.V1;
using ImageServer.SDK.Core.Configuration;
using ImageServer.SDK.Core.Authentications.Models;
using ImageServer.SDK.V1.QueryEngines.Models;
using ImageServer.SDK.V1.QueryEngineProxies.Models;
// 1. 创建客户端
var config = new ImageServerClientConfig
{
BaseUrl = "https://your-server.com",
Timeout = TimeSpan.FromSeconds(30)
};
var client = new ImageServerV1Client(config);
// 2. 登录
var loginRequest = new LoginRequest
{
Username = "admin",
Password = "1q2w3E*"
};
var loginResult = await client.Authentication.LoginAsync(loginRequest);
client.SetAccessToken(loginResult.AccessToken);
Console.WriteLine($"登录成功,令牌将在 {loginResult.ExpiresIn} 秒后过期");
// 3. 获取所有租户
var tenants = await client.Tenant.GetAllTenantsAsync();
var firstTenant = tenants.First();
Console.WriteLine($"选择租户: {firstTenant.Name} (代码: {firstTenant.Code})");
// 4. 设置租户上下文
client.SetTenantId(firstTenant.Id.ToString());
// 5. 获取用户信息
var profile = await client.UserInfo.GetMyProfileAsync();
Console.WriteLine($"当前用户: {profile.UserName} ({profile.Email})");
// 6. 获取查询引擎列表
var engines = await client.QueryEngine.GetAllQueryEnginesAsync();
Console.WriteLine($"找到 {engines.Count} 个查询引擎");
foreach (var engine in engines)
{
Console.WriteLine($"- {engine.Title} (Provider: {engine.Provider})");
}
// 7. 创建新查询引擎
var createEngineDto = new CreateOrUpdateQueryEngineDto
{
Name = "MyQueryEngine",
Title = "我的查询引擎",
Provider = "ApiSource",
IsMultiTenant = true,
StudyQuerySupport = true,
ImageQuerySupport = true,
ModalityImageQuerySupport = false,
ImageDownloadSupport = true,
ImageQueryStyle = 1,
ModalityImageQueryStyle = 1,
Items = new List<CreateOrUpdateQueryEngineItemDto>
{
new() { Name = "BaseUrl", Value = "https://api.example.com" },
new() { Name = "ApiKey", Value = "your-api-key" }
}
};
var newEngine = await client.QueryEngine.CreateQueryEngineAsync(createEngineDto);
Console.WriteLine($"创建查询引擎成功: {newEngine.Id}");
// 8. 创建查询引擎代理
var createProxyDto = new CreateOrUpdateQueryEngineProxyDto
{
QuerierId = newEngine.Id,
DisplayName = "测试代理",
HospitalCode = firstTenant.Code
};
var newProxy = await client.QueryEngineProxy.CreateQueryEngineProxyAsync(createProxyDto);
Console.WriteLine($"创建代理成功: {newProxy.Id}");
V2 客户端完整工作流
using ImageServer.SDK.V2;
using ImageServer.SDK.V2.BaseInfoEndpoints.Models;
// 1. 创建客户端
var config = new ImageServerClientConfig
{
BaseUrl = "https://your-server.com",
Timeout = TimeSpan.FromSeconds(30)
};
var client = new ImageServerV2Client(config);
// 2. 登录 (与 V1 相同)
var loginResult = await client.Authentication.LoginAsync(loginRequest);
client.SetAccessToken(loginResult.AccessToken);
// 3. 创建 BaseInfo 端点
var endpointDto = new CreateOrUpdateBaseInfoEndpointDto
{
Name = "HospitalBaseInfo",
DisplayName = "医院基础信息 API",
BaseUrl = "https://hospital-api.com",
Route = new CreateOrUpdateRouteDto
{
Path = "/api/baseinfo",
Method = "GET"
},
Headers = new List<CreateOrUpdateHeaderDto>
{
new() { Key = "Accept", Value = "application/json" },
new() { Key = "X-API-Version", Value = "1.0" }
},
Parameters = new List<CreateOrUpdateParameterDto>
{
new() { Name = "tenantId", Value = "{tenantId}", Type = "Query" }
}
};
var endpoint = await client.BaseInfoEndpoint.CreateAsync(endpointDto);
Console.WriteLine($"创建端点成功: {endpoint.DisplayName}");
// 4. 获取所有端点
var allEndpoints = await client.BaseInfoEndpoint.GetListAsync();
foreach (var ep in allEndpoints)
{
Console.WriteLine($"端点: {ep.DisplayName} - {ep.BaseUrl}{ep.Route.Path}");
}
// 5. 查询特定端点
var foundEndpoint = await client.BaseInfoEndpoint.FindByNameAsync("HospitalBaseInfo");
if (foundEndpoint != null)
{
Console.WriteLine($"找到端点: {foundEndpoint.DisplayName}");
}
💡 最佳实践
1. 令牌管理
// 检查令牌是否即将过期
if (loginResult.RemainingLifetime < TimeSpan.FromMinutes(5))
{
// 重新登录
loginResult = await client.Authentication.LoginAsync(loginRequest);
client.SetAccessToken(loginResult.AccessToken);
}
2. 错误处理
using ImageServer.SDK.Core.Exceptions;
try
{
var result = await client.Authentication.LoginAsync(request);
}
catch (AuthenticationException ex)
{
Console.WriteLine($"认证失败: {ex.Message}");
}
catch (ImageServerException ex)
{
Console.WriteLine($"API 错误: {ex.Message}");
Console.WriteLine($"状态码: {ex.StatusCode}");
}
catch (HttpRequestException ex)
{
Console.WriteLine($"网络错误: {ex.Message}");
}
3. 资源清理
// 非 DI 模式下,虽然 HttpClient 被缓存,但建议在应用关闭时清理
// DI 模式下由容器自动管理
📝 许可证
本项目采用 MIT 许可证。
🤝 贡献
欢迎提交 Issue 和 Pull Request!
贡献指南
- Fork 本仓库
- 创建特性分支 (
git checkout -b feature/AmazingFeature) - 提交更改 (
git commit -m 'Add some AmazingFeature') - 推送到分支 (
git push origin feature/AmazingFeature) - 开启 Pull Request
📧 联系方式
- Issues: GitHub Issues
- Documentation: SDK_USAGE_GUIDE.md
最后更新: 2025-11-04 版本: v1.0.0-beta 状态: ✅ 核心功能已完成,测试进行中
| Product | Versions 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 was computed. 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 was computed. 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 was computed. net48 was computed. 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. |
-
.NETFramework 4.6.2
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.10)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Http (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
-
.NETStandard 2.0
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.10)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Http (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
-
net9.0
- Microsoft.Extensions.Configuration.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Configuration.Binder (>= 9.0.10)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.10)
- Microsoft.Extensions.Http (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.