ImageServer.SDK 0.6.0

dotnet add package ImageServer.SDK --version 0.6.0
                    
NuGet\Install-Package ImageServer.SDK -Version 0.6.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="ImageServer.SDK" Version="0.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ImageServer.SDK" Version="0.6.0" />
                    
Directory.Packages.props
<PackageReference Include="ImageServer.SDK" />
                    
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 ImageServer.SDK --version 0.6.0
                    
#r "nuget: ImageServer.SDK, 0.6.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 ImageServer.SDK@0.6.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=ImageServer.SDK&version=0.6.0
                    
Install as a Cake Addin
#tool nuget:?package=ImageServer.SDK&version=0.6.0
                    
Install as a Cake Tool

ImageServer SDK

C# 客户端 SDK for ImageServer V1 & V2 APIs

.NET License Build

📋 概述

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);
    }
}

📚 文档导航

核心文档

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+ 应用

典型应用场景

  1. 医疗影像系统集成 - 连接多个医院的 ImageServer,实现跨院影像共享
  2. PACS 系统对接 - 管理 PACS 端点和查询引擎配置
  3. 多租户 SaaS 平台 - 租户隔离和动态切换,支持多医院部署
  4. 数据同步服务 - 后台定时同步租户数据和影像信息
  5. 监控和告警系统 - 监控 ImageServer 健康状态和性能指标
  6. 影像检索服务 - 基于查询引擎实现高级影像检索功能

🛠️ 技术栈

目标框架

  • .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!

贡献指南

  1. Fork 本仓库
  2. 创建特性分支 (git checkout -b feature/AmazingFeature)
  3. 提交更改 (git commit -m 'Add some AmazingFeature')
  4. 推送到分支 (git push origin feature/AmazingFeature)
  5. 开启 Pull Request

📧 联系方式


最后更新: 2025-11-04 版本: v1.0.0-beta 状态: ✅ 核心功能已完成,测试进行中

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 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. 
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
0.6.0 394 11/12/2025
0.5.0 327 11/12/2025
0.4.0 335 11/12/2025
0.3.0 255 11/5/2025
0.2.0 262 11/4/2025
0.1.0 251 11/4/2025