EF Core Interceptors: Supercharge Your Data Access with the Decorator Pattern

Leader posted 3 min read

Learn how to hook into EF Core operations using interceptors, leverage the Decorator Pattern for clean cross-cutting concerns, and see real-world examples like SQL logging and auditing.

Table of Contents

  • A Brief History of EF Core Interceptors

  • What Are EF Core Interceptors?

  • EF Core Interceptors and the Decorator Pattern

  • Top Use Cases for EF Core Interceptors

  • Example 1: Logging SQL Queries

  • Example 2: Auditing SaveChanges

  • Final Thoughts

A Brief History of EF Core Interceptors

EF Core interceptors were first introduced in EF Core 3.0 (released in September 2019). This version marked a significant step forward in EF Core's extensibility, allowing developers to intercept low-level database operations like commands, connections, and transactions.

Since then, Microsoft has expanded interceptor capabilities in newer versions:

EF Core 5.0: Introduced SaveChanges interceptors for tracking changes at the context level.

EF Core 6.0+: Continued performance improvements and better support for asynchronous interception.

What Are EF Core Interceptors?

In Entity Framework Core (EF Core), interceptors allow developers to hook into the execution pipeline of EF Core's operations. Think of them as middleware for your database interactions. You can intercept events such as:

  1. SQL command execution

  2. Connection opening/closing

  3. Transactions

  4. SaveChanges lifecycle

This lets you inject custom logic like logging, auditing, validation, or performance metrics without modifying your application’s core logic.

EF Core Interceptors and the Decorator Pattern

EF Core interceptors are a real-world application of the Decorator Pattern, a classic design pattern where functionality is wrapped around existing behavior to extend or alter it.

The core EF operation is the base object.
Your interceptor is the decorator.
You add behavior before or after EF Core’s operation — without altering EF’s core.

This approach promotes clean code, separation of concerns, and makes your custom behavior reusable and testable.

Top Use Cases for EF Core Interceptors

– SQL Query Logging

– Track database queries for performance or debugging.

– Auditing – Automatically log user actions and timestamps.

– Soft Deletes – Intercept delete commands and convert them to updates.

– Command Modification – Change or enrich raw SQL before execution.

– Multi-Tenant Filtering – Inject filters dynamically for multi-tenant scenarios.

Example 1: Logging SQL Queries

Let’s log every SQL command executed by EF Core.

Step 1: Create a SQL Logging Interceptor

using Microsoft.EntityFrameworkCore.Diagnostics;
using System.Diagnostics;

public class SqlLoggingInterceptor : DbCommandInterceptor
{
    public override InterceptionResult<DbDataReader> ReaderExecuting(
        DbCommand command, 
        CommandEventData eventData, 
        InterceptionResult<DbDataReader> result)
    {
        Debug.WriteLine($"SQL Executed: {command.CommandText}");
        return base.ReaderExecuting(command, eventData, result);
    }
}

Output:

SQL Executed: SELECT * FROM [Users] WHERE [IsActive] = 1

Step 2: Register the Interceptor

public class AppDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer("YourConnectionString")
            .AddInterceptors(new SqlLoggingInterceptor());
    }
}

Example 2: Auditing SaveChanges

Let’s implement automatic auditing of entity modifications.

Step 1: Define an Auditable Entity Base Class

public abstract class AuditableEntity
{
    public DateTime ModifiedAt { get; set; }
    public string ModifiedBy { get; set; }
}

Step 2: Implement a SaveChangesInterceptor

public class AuditInterceptor : SaveChangesInterceptor
{
    private readonly string _currentUser;

    public AuditInterceptor(string currentUser)
    {
        _currentUser = currentUser;
    }

    public override int SavingChanges(DbContextEventData eventData, int result)
    {
        var context = eventData.Context;
        if (context == null) return base.SavingChanges(eventData, result);

        var entries = context.ChangeTracker
            .Entries<AuditableEntity>()
            .Where(e => e.State == EntityState.Modified);

        foreach (var entry in entries)
        {
            entry.Entity.ModifiedAt = DateTime.UtcNow;
            entry.Entity.ModifiedBy = _currentUser;
        }

        return base.SavingChanges(eventData, result);
    }
}

Final Thoughts

EF Core interceptors are an underutilized gem in the .NET ecosystem. They let you cleanly implement cross-cutting concerns without muddying your core application code. And because they follow the Decorator Pattern, you get flexibility, maintainability, and testability.

Whether you need to log queries, audit changes, enforce soft deletes, or handle custom behavior, interceptors offer a powerful and elegant solution.

References
EF Core Documentation – Interceptors
https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors

EF Core 3.0 Release Notes (Interceptors introduced)
https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/whatsnew

EF Core SaveChanges Interceptor Docs
https://learn.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors#savechanges-interception

Decorator Pattern - Refactoring Guru (for pattern explanation)
https://refactoring.guru/design-patterns/decorator

EF Core GitHub Repository (for latest updates and issues)
https://github.com/dotnet/efcore

If you read this far, tweet to the author to show them you care. Tweet a Thanks
0 votes
0 votes

More Posts

Reliable Messaging in .NET: Domain Events and the Outbox Pattern with EF Core Interceptors

Spyros - Oct 13

⚔️ The Mighty MERGE: Using SQL Merge Statements Safely with EF Core

Spyros - Jul 25

EF Core: Lazy Loading, Eager Loading, and Loading Data on Demand

Spyros - Mar 6

Supercharging EF Core Specifications with EF.CompileQuery

Spyros - Aug 5

EF Core Bulk Updates and Deletes: Performance vs Change Tracking

Spyros - Mar 24
chevron_left