Filter by title

Interceptors

ABP provides a powerful interception system that allows you to execute custom logic before and after method calls without modifying the original method code. This is achieved through dynamic proxying and is extensively used throughout the ABP framework to implement cross-cutting concerns. ABP's interception is implemented on top of the Castle DynamicProxy library.

What is Dynamic Proxying / Interception?

Interception is a technique that allows executing additional logic before or after a method call without directly modifying the method's code. This is achieved through dynamic proxying, where the runtime generates proxy classes that wrap the original class.

When a method is called on a proxied object:

  1. The call is intercepted by the proxy
  2. Custom behaviors (like logging, validation, or authorization) can be executed
  3. The original method is called
  4. Additional logic can be executed after the method completes

This enables cross-cutting concerns (logic that applies across many parts of an application) to be handled in a clean, reusable way without code duplication.

Similarities and Differences with MVC Action/Page Filters

If you are familiar with ASP.NET Core MVC, you've likely used action filters or page filters. Interceptors are conceptually similar but have some key differences:

Similarities

  • Both allow executing code before and after method execution
  • Both are used to implement cross-cutting concerns like validation, logging, caching, or exception handling
  • Both support asynchronous operations

Differences

  • Scope: Filters are tied to MVC's request pipeline, while interceptors can be applied to any class or service in the application
  • Configuration: Filters are configured via attributes or middleware in MVC, while interceptors in ABP are applied through dependency injection and dynamic proxies
  • Target: Interceptors can target application services, domain services, repositories, and virtually any service resolved from the IoC container—not just web controllers

How ABP Uses Interceptors

ABP Framework extensively leverages interception to provide built-in features without requiring boilerplate code. Here are some key examples:

Unit of Work (UOW)

Automatically begins and commits/rolls back a database transaction when entering or exiting an application service method. This ensures data consistency without manual transaction management.

Input Validation

Input DTOs are automatically validated against data annotation attributes and custom validation rules before executing the service logic, providing consistent validation behavior across all services.

Authorization

Checks user permissions before allowing the execution of application service methods, ensuring security policies are enforced consistently.

Feature & Global Feature Checking

Checks if a feature is enabled before executing the service logic, allowing you to conditionally enable or restrict functionality for tenants or the application.

Auditing

Automatically logs who performed an action, when it happened, what parameters were used, and what data was involved, providing comprehensive audit trails.

Building Your Own Interceptor

You can create custom interceptors in ABP to implement your own cross-cutting concerns.

Creating an Interceptor

Create a class that inherits from AbpInterceptor:

using System.Threading.Tasks;
using Volo.Abp.Aspects;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;

public class ExecutionTimeLogInterceptor : AbpInterceptor, ITransientDependency
{
    private readonly ILogger<ExecutionTimeLogInterceptor> _logger;

    public ExecutionTimeLogInterceptor(ILogger<ExecutionTimeLogInterceptor> logger)
    {
        _logger = logger;
    }

    public override async Task InterceptAsync(IAbpMethodInvocation invocation)
    {
        var sw = Stopwatch.StartNew();

        _logger.LogInformation($"Executing {invocation.TargetObject.GetType().Name}.{invocation.Method.Name}");

        // Proceed to the actual target method
        await invocation.ProceedAsync();

        sw.Stop();

        _logger.LogInformation($"Executed {invocation.TargetObject.GetType().Name}.{invocation.Method.Name} in {sw.ElapsedMilliseconds} ms");
    }
}

Register Interceptors

Create a static class that contains the RegisterIfNeeded method and register the interceptor in the PreConfigureServices method of your module.

The ShouldIntercept method is used to determine if the interceptor should be registered for the given type. You can add an IExecutionTimeLogEnabled interface and implement it in the classes that you want to intercept.

DynamicProxyIgnoreTypes is static class that contains the types that should be ignored by the interceptor. See Performance Considerations for more information.

// Define an interface to mark the classes that should be intercepted
public interface IExecutionTimeLogEnabled
{
}
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;

// A simple service that added to the DI container and will be intercepted since it implements the `IExecutionTimeLogEnabled` interface
public class SampleExecutionTimeService : IExecutionTimeLogEnabled, ITransientDependency
{
    public virtual async Task DoWorkAsync()
    {
        // Simulate a long-running task to test the interceptor
        await Task.Delay(1000);
    }
}
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;

public static class ExecutionTimeLogInterceptorRegistrar
{
    public static void RegisterIfNeeded(IOnServiceRegistredContext context)
    {
        if (ShouldIntercept(context.ImplementationType))
        {
            context.Interceptors.TryAdd<ExecutionTimeLogInterceptor>();
        }
    }

    private static bool ShouldIntercept(Type type)
    {
        return !DynamicProxyIgnoreTypes.Contains(type) && typeof(IExecutionTimeLogEnabled).IsAssignableFrom(type);
    }
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
    context.Services.OnRegistered(ExecutionTimeLogInterceptorRegistrar.RegisterIfNeeded);
}

Restrictions and Important Notes

Always use asynchronous methods

For best performance and reliability, implement your service methods as asynchronous to avoid async over sync, that can cause unexpected problems, For more information, see Should I expose synchronous wrappers for asynchronous methods?

Virtual Methods Requirement

For class proxies, methods need to be marked as virtual so that they can be overridden by the proxy. Otherwise, interception will not occur.

public class MyService : IExecutionTimeLogEnabled, ITransientDependency
{
    // This method CANNOT be intercepted (not virtual)
    public void CannotBeIntercepted()
    {
    }

    // This method CAN be intercepted (virtual)
    public virtual void CanBeIntercepted()
    {
    }
}

This restriction does not apply to interface-based proxies. If your service implements an interface and is injected via that interface, all methods can be intercepted regardless of the virtual keyword.

Dependency Injection Scope

Interceptors only work when services are resolved from the dependency injection container. Direct instantiation with new bypasses interception:

// This will NOT be intercepted
var service = new MyService();
service.CannotBeIntercepted();

// This WILL be intercepted (if MyService is registered with DI)
var service = serviceProvider.GetService<MyService>();
service.CanBeIntercepted();

Performance Considerations

Interceptors are generally efficient, but each one adds method-call overhead. Keep the number of interceptors minimal on hot paths.

Castle DynamicProxy can negatively impact performance for certain components, notably ASP.NET Core MVC controllers. See the discussions in castleproject/Core#486 and abpframework/abp#3180.

ABP uses interceptors for features like UOW, auditing, and authorization, which rely on dynamic proxy classes. For controllers, prefer implementing cross-cutting concerns with middleware or MVC/Page filters instead of dynamic proxies.

To avoid generating dynamic proxies for specific types, use the static class DynamicProxyIgnoreTypes and add the base classes of the types to the list. Subclasses of any listed base class are also ignored. ABP framework already adds some base classes to the list (ComponentBase, ControllerBase, PageModel, ViewComponent); you can add more base classes if needed.

Always use interface-based proxies instead of class-based proxies for better performance.

See Also

Contributors


Last updated: January 01, 0001 Edit this page on GitHub

Was this page helpful?

Please make a selection.

To help us improve, please share your reason for the negative feedback in the field below.

Please enter a note.

Thank you for your valuable feedback!

Please note that although we cannot respond to feedback, our team will use your comments to improve the experience.

In this document
ABP Community Talks
Beyond WordPress: A Developer-Friendly CMS for .NET
28 Aug, 17:00
Online
Watch the Event
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book