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:
SQL command execution
Connection opening/closing
Transactions
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