根本原因是代理对象调用目标方法时异常被包装为targetinvocationexception,掩盖原始异常堆栈;需在intercept中用try/catch捕获并设invocation.exception,异步方法必须用proceedasync,接口代理不支持sealed类或非接口类型,类代理要求方法为virtual,di注册时须显式创建代理而非直接注入原始服务。

为什么直接用 Castle.DynamicProxy 写 AOP 容易报 System.Reflection.TargetInvocationException
根本原因是代理对象调用目标方法时,异常被包装了一层。你看到的不是原始异常,而是外层包装——这会让调试时找不到真实出错位置。
实操建议:
- 始终在拦截器的
Proceed()调用前后加try/catch,并用invocation.Exception = ex显式设置异常,避免丢失堆栈 - 不要在拦截器里直接
throw new Exception(),改用invocation.ReturnValue = ...或invocation.Proceed()后再处理 - 若目标方法是
async Task,必须用invocation.ProceedAsync(),否则会同步阻塞或丢弃上下文
接口代理和类代理的区别到底影响什么
接口代理(ProxyGenerator.CreateInterfaceProxyWithTarget)只支持接口类型,不能代理私有方法、字段或 sealed 类;类代理(ProxyGenerator.CreateClassProxy)要求目标类至少有一个 public/protected 构造函数,且所有要拦截的方法必须是 virtual 或 abstract。
常见错误现象:
- 类代理时方法没加
virtual→ 拦截器完全不触发,静默失效 - 用接口代理传入一个 class 实例 → 运行时报
ArgumentException: Type is not an interface - 代理
sealed类 → 直接抛NotSupportedException
性能提示:接口代理比类代理快约 15–20%,因为无需生成子类字节码。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
IInterceptor 中如何区分同步/异步方法调用
不能靠 invocation.Method.ReturnType == typeof(Task) 粗判——泛型 Task<t></t>、ValueTask、async void 都需要单独识别。
实操建议:
- 检查
invocation.Method.IsDefined(typeof(AsyncStateMachineAttribute), false)判定是否为编译器生成的异步方法 - 对
Task和Task<t></t>,统一走await invocation.ProceedAsync();对普通同步方法,用invocation.Proceed() - 避免在拦截器里用
.Wait()或.Result强制同步,容易引发死锁(尤其在 ASP.NET Core 同步上下文中)
在 ASP.NET Core 里注册 Castle DynamicProxy 拦截器要注意什么
DI 容器本身不理解代理逻辑,所以不能把拦截器当普通 service 注册后直接注入到控制器——那样拿到的是原始实例,不是代理对象。
正确做法是结合 Scrutor 或手动包装:
- 用
services.AddScoped<iservice serviceimpl>()</iservice>注册原始实现 - 再通过
services.AddTransient<iinterceptor logginginterceptor>()</iinterceptor>注册拦截器 - 最后用工厂委托显式创建代理:
services.AddScoped<iservice>(sp => new ProxyGenerator().CreateInterfaceProxyWithTarget(...))</iservice>
容易被忽略的一点:拦截器生命周期必须与目标服务一致。比如目标是 Scoped,拦截器也得是 Scoped,否则可能跨请求复用状态。










