Proposal: Separate Castle.DynamicProxy dependency from ABP

Author: neominkyCreated Jun 30, 2026Updated Jul 1, 2026

Hi everyone,

I would like to discuss separating the Castle Windsor / Castle.DynamicProxy dependency from ABP.

My long-term goal is to make ABP compatible with NativeAOT.

There are several reasons why I think this is worth considering:

  • Castle has performance overhead compared to other IoC.
  • Castle is no longer actively maintained.
  • It does not integrate cleanly with modern technologies such as .NET Aspire.
  • NativeAOT compatibility is very difficult while relying on runtime proxy generation.

I know this topic has come up before:

https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5604

Current progress

As a proof of concept, I successfully separated the built-in interceptors that depend on Castle.DynamicProxy into a separate package named Abp.Interception.Castle.

Instead of moving all interceptor implementations (which would duplicate code and make maintenance difficult), I only moved the registrar classes (for example, AuthorizationInterceptorRegistrar).

This allows the built-in interceptor implementations to remain shared while only the Castle-specific registration logic lives in the new package.

To make this possible, I introduced a new abstraction named IAbpInvocation, which replaces Castle.DynamicProxy.IInvocation.

Interceptor implementation

Instead of using runtime proxy generation, I implemented interception using a C# Source Generator.

The generated code injects the interceptor pipeline directly into the target classes at compile time.

Although there is a much simpler implementation, I intentionally chose a more complex internal implementation in order to preserve source-level compatibility as much as possible.

Compatibility

The good news is that almost all existing interceptor implementations continue to work.

In most cases, the only required change is replacing:

Castle.DynamicProxy.IInvocation

with

IAbpInvocation

The required change is minimal:

diff
- public void Intercept(IInvocation invocation)
+ public void Intercept(IAbpInvocation invocation)

You can see the actual changes here:

Replace IInvocation with IAbpInvocation diff

However, since the main ABP package would no longer expose Castle.DynamicProxy.IInvocation, this would still be a breaking change.

I also think it should be possible to provide a source generator (not implemented yet) that automatically rewrites IInvocation references to IAbpInvocation, minimizing migration effort.

There may also need to be a startup extension such as UseCastle() to enable the Castle-based implementation. I'm still considering the best design for that.

Questions

I'd like to hear your thoughts.

Would you support the following direction?

  • Separate the Castle.DynamicProxy dependency from the core ABP package.
  • Introduce IAbpInvocation into the ABP package as the common abstraction.
  • Eventually remove the Castle dependency from the core package completely.

Does this overall direction make sense?

I also have a couple of design questions:

  1. Do you agree with moving the Castle.DynamicProxy dependency into Abp.Interception.Castle?
  2. Should the built-in interceptor implementations remain in the core ABP package as shared/common code, or would it be better to move them entirely into Abp.Interception.Castle?

Working repository

My work-in-progress repository:

https://github.com/neominky/aspnetboilerplate/

Compile-time interception sample:

https://github.com/neominky/aspnetboilerplate/tree/Replace-Castle-DynamicProxy/test/Abp.Interception.CompileTime.Host

Tests:

https://github.com/neominky/aspnetboilerplate/tree/Replace-Castle-DynamicProxy/test/Abp.Interception.CompileTime.Tests

I'd really appreciate your feedback before I continue with this work.

Source: aspnetboilerplate/aspnetboilerplate