Galosys.Foundation.Castle.Core
26.9.16.1
dotnet add package Galosys.Foundation.Castle.Core --version 26.9.16.1
NuGet\Install-Package Galosys.Foundation.Castle.Core -Version 26.9.16.1
<PackageReference Include="Galosys.Foundation.Castle.Core" Version="26.9.16.1" />
<PackageVersion Include="Galosys.Foundation.Castle.Core" Version="26.9.16.1" />
<PackageReference Include="Galosys.Foundation.Castle.Core" />
paket add Galosys.Foundation.Castle.Core --version 26.9.16.1
#r "nuget: Galosys.Foundation.Castle.Core, 26.9.16.1"
#:package Galosys.Foundation.Castle.Core@26.9.16.1
#addin nuget:?package=Galosys.Foundation.Castle.Core&version=26.9.16.1
#tool nuget:?package=Galosys.Foundation.Castle.Core&version=26.9.16.1
Galosys.Foundation.Castle.Core
成熟度: 🟢 稳定 — 生产可用,测试充分,活跃维护
基于 Castle.DynamicProxy 的 AOP 拦截模块。核心设计理念:
- 原生 Castle 集成 — 直接使用 Castle 的
IInterceptor/IInvocation/ProxyGenerator - 继承式拦截 — 拦截器特性继承
InterceptorAttribute覆写InterceptAsync,无需独立 Handler 类 - 单一适配器 —
InterceptorAdapter一个IInterceptor实现,自动发现方法上的所有InterceptorAttribute子类并执行链式分派
架构概览
用户代码 → [MyAttribute] 标记 → DI 自动代理注册 → Castle 生成代理 → 方法调用 → InterceptorAdapter.Intercept() → GetCustomAttributes<InterceptorAttribute>() → MyAttribute.InterceptAsync() → invocation.Proceed() → 业务方法
┌─ Service 层 ─────────────────────────────────────────────┐
│ [Transactional] │
│ public class OrderService { │
│ public virtual async Task CreateAsync() { ... } │
│ } │
└──────────────────────────┬───────────────────────────────┘
│ DI 注入(Castle 自动代理)
▼
┌─ Castle 代理 ────────────────────────────────────────────┐
│ ProxyGenerator.CreateClassProxy / CreateInterfaceProxy │
│ → InterceptorAdapter.Intercept() │
│ → GetCustomAttributes<InterceptorAttribute>() │
│ → TransactionalAttribute.InterceptAsync(invocation, sp)│
│ → invocation.ProceedAsync() │
└──────────────────────────────────────────────────────────┘
安装
dotnet add package Galosys.Foundation.Castle.Core
定义拦截器
继承 InterceptorAttribute 并覆写 InterceptAsync 方法:
using Castle.DynamicProxy;
public class AuditLogAttribute : InterceptorAttribute
{
public override async ValueTask InterceptAsync(IAsyncInvocation invocation, IServiceProvider sp)
{
Console.WriteLine($"[Audit] 调用: {invocation.Invocation.Method.Name}");
await invocation.ProceedAsync();
Console.WriteLine($"[Audit] 返回: {invocation.Result}");
}
}
public class PaymentService
{
[AuditLog]
public virtual async Task PayAsync(decimal amount)
{
await Task.CompletedTask;
}
}
InterceptorAttribute 自动处理同步 void / Task / ValueTask / Task<T> / ValueTask<T> 所有返回类型。
内置拦截器
[Transactional] — 事务管理(Castle.DynamicProxy)
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
IsolationLevel |
IsolationLevel |
ReadCommitted |
事务隔离级别 |
TransactionScopeOption |
TransactionScopeOption |
Required |
事务范围选项 |
Timeout |
long (ms) |
60000 |
超时时间(毫秒) |
[Transactional]
public virtual async Task CreateOrderAsync(Order order) { }
[Transactional(IsolationLevel = IsolationLevel.Serializable, Timeout = 30000)]
public virtual void ProcessPayment(Payment payment) { }
[Retryable] — 重试策略(扩展拦截器,物理归属 Galosys.Foundation.Polly.Core)
物理归属:自 v2026.09 起,
RetryableAttribute从Galosys.Foundation.Castle.Core迁至Galosys.Foundation.Polly.Core(依赖反转)。namespaceCastle.DynamicProxy保留,源码using Castle.DynamicProxy;仍可编译。运行时使用[Retryable]必须额外引用Galosys.Foundation.Polly.Core,否则解析到 Core NoOp,重试无效果。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
MaxAttempts |
int |
3 |
最大尝试次数 |
MaxDelay |
int (ms) |
5000 |
基础延迟(毫秒) |
Multiplier |
double |
2 |
指数退避乘数 |
RetryFor |
Type |
typeof(Exception) |
仅对指定异常类型重试 |
[Retryable]
public virtual async Task<string> CallApiAsync() { }
[Retryable(MaxAttempts = 5, MaxDelay = 10000, Multiplier = 2)]
public virtual async Task<Data> FetchDataAsync() { }
[Retryable(RetryFor = typeof(HttpRequestException))]
public virtual async Task<Response> ResilientCallAsync() { }
API 演进:上述
MaxAttempts/MaxDelay/Multiplier/RetryFor参数已标记[Obsolete],重试参数应统一在services.AddResilienceCore(RetryableAttribute.PipelineName, b => b.WithRetry(...))中配置,然后services.AddPollyResilience()覆盖 NoOp。[Retryable]拦截器仅作为命名管道的入口,不再直接持有 Polly 配置。// 注册命名弹性管道 builder.Services.AddResilienceCore(RetryableAttribute.PipelineName, b => b.WithRetry(new RetryOptions { MaxRetries = 5, Delay = TimeSpan.FromSeconds(1), BackoffType = BackoffType.Exponential, })); builder.Services.AddPollyResilience(); // 覆盖 NoOp,启用 Polly 真实实现模块化宿主无需手动注册:引
Galosys.Foundation.Polly.Core后PollyCoreModule自动注入默认retry命名管道 +AddPollyResilience()。详细管道配置见Galosys.Foundation.Polly.Core的弹性管道实现。
注册方式
方式一:自动注册(推荐)
// Program.cs
builder.Host.UseCastleCoreServiceProvider();
内部自动执行 services.ConfigureCastleDynamicProxy(),将所有标注了 InterceptorAttribute 子类的服务实现替换为 Castle 代理。
方式二:手动注册
// 需在所有服务注册完成后调用
builder.Services.ConfigureCastleDynamicProxy();
接口代理 vs 类代理
| 接口代理 | 类代理 | |
|---|---|---|
| 要求 | 实现接口 | 类 + virtual 方法 |
| 示例 | IService + Service |
class Service { virtual void X() } |
| 虚方法 | 不需要 | 必须 virtual |
| 拦截范围 | 接口定义的方法 | 所有 public virtual 方法 |
| 性能 | 略快 | 略慢 |
类代理限制:非 virtual 的 public 方法静默跳过代理(不拦截、无提示)。启动时 ConfigureCastleDynamicProxy 会对非虚方法输出 Trace 警告。
异步支持
InterceptorAdapter 统一处理 5 种返回类型:
| 返回类型 | 处理方式 |
|---|---|
void |
同步路径,GetAwaiter().GetResult() |
Task |
chain().AsTask() 返回给 Castle |
Task<T> |
WrapTaskResult<T> 提取结果 |
ValueTask |
chain() 直接作为 ReturnValue |
ValueTask<T> |
WrapValueTaskResult<T> 提取结果 |
拦截器链
多个 Attribute 叠加时,按方法上的声明顺序执行(从外到内)。
// 执行顺序:1) Transactional → 2) Retryable
[Transactional]
[Retryable]
public virtual async Task ComplexOperationAsync()
{
// 业务方法在最内层执行
}
核心类参考
| 类 | 命名空间 | 说明 |
|---|---|---|
InterceptorAttribute |
Castle.DynamicProxy |
拦截器基类,继承 Attribute,抽象 InterceptAsync(IAsyncInvocation, IServiceProvider) |
InterceptorAdapter |
Castle.DynamicProxy |
单一 IInterceptor 实现,自动发现方法上的 InterceptorAttribute 子类 |
IAsyncInvocation |
Castle.DynamicProxy |
异步调用上下文,ProceedAsync() 调用下一管道 |
TransactionalAttribute |
Castle.DynamicProxy |
事务拦截器 |
RetryableAttribute |
Castle.DynamicProxy |
重试拦截器,解析 IResiliencePipelineProvider 命名管道 PipelineName |
CastleCoreServiceCollectionExtensions |
Microsoft.Extensions.DependencyInjection |
ConfigureCastleDynamicProxy() |
CastleCoreHostBuilderExtensions |
Microsoft.Extensions.Hosting |
UseCastleCoreServiceProvider() |
测试覆盖
集成测试位于 framework/test/Galosys.Foundation.Castle.Core.Tests/,涵盖:
ConfigureCastleDynamicProxy— ProxyGenerator 单例注册、三种注册方式的代理替换(ImplementationType / Instance / Factory)- 生命周期保留 — Singleton / Scoped / Transient 在代理替换后行为不变
- 代理类型 — 接口代理(ITestService)、类代理(TestClassService)
- 拦截器链 — 多 Attribute 叠加执行
- 异步支持 — void / Task / Task<T> / ValueTask / ValueTask<T>
CastleCoreServiceProviderFactory— IHostBuilder 集成链路
运行测试:
dotnet test framework/test/Galosys.Foundation.Castle.Core.Tests/
性能基线
BenchmarkDotNet v0.13.12 / .NET 10.0.1 / RyuJIT AVX2(framework/test/Galosys.Foundation.Castle.Core.Benchmarks/)
| 场景 | Mean | 相对基线 |
|---|---|---|
| 直接调用(基线) | 3.6 ns | 1.00x |
| Castle 代理 + Noop 拦截器 | 16.2 ns | 4.50x |
| InterceptorAdapter 直通(无匹配) | 3.8 ns | 1.06x |
[Transactional] |
3.8 ns | 1.06x |
[Retryable] |
7.0 ns | 1.94x |
注意事项
- 虚方法:类代理必须
virtual,否则拦截静默跳过 - 注册顺序:
ConfigureCastleDynamicProxy必须在所有服务注册之后调用(替换式注册) - ValidateScopes:
UseCastleCoreServiceProvider()默认启用;若代理的 Singleton 服务依赖 Scoped 构造参数,会抛出InvalidOperationException,包含具体提示 - 拦截器嵌套:拦截器内不要创建新代理
- AOT:Castle.Core 基于 Emit,Native AOT 不可用
依赖
- Castle.Core 5.1.1
- Galosys.Foundation.Core
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- castle.core (>= 5.1.1)
- Galosys.Foundation.Core (>= 26.9.16.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Galosys.Foundation.Castle.Core:
| Package | Downloads |
|---|---|
|
Galosys.Foundation.Polly.Core
Galosys.Foundation快速开发库 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 26.9.16.1 | 97 | 9/16/2026 |
| 26.9.15.1 | 96 | 9/15/2026 |
| 26.9.14.1 | 96 | 9/14/2026 |
| 26.9.10.1 | 91 | 9/10/2026 |
| 26.9.3.1 | 94 | 9/3/2026 |
| 26.8.29.1 | 86 | 8/31/2026 |
| 26.8.26.1 | 93 | 8/26/2026 |
| 26.8.23.1 | 92 | 8/23/2026 |
| 26.8.21.1 | 89 | 8/21/2026 |
| 26.8.20.1 | 85 | 8/20/2026 |
| 26.8.18.1 | 96 | 8/18/2026 |
| 26.8.17.1 | 98 | 8/17/2026 |
| 26.8.13.2 | 96 | 8/13/2026 |
| 26.8.13.1 | 95 | 8/13/2026 |
| 26.8.12.2 | 97 | 8/12/2026 |
| 26.8.12.1 | 96 | 8/12/2026 |
| 26.8.10.1 | 95 | 8/10/2026 |
| 26.8.5.1 | 100 | 8/5/2026 |
| 26.8.4.1 | 96 | 8/4/2026 |
| 26.8.3.1 | 104 | 8/3/2026 |