How to Implement EF Core-Like Interceptors in .NET Framework with EF6
When working with .NET Core and EF Core, it's easy to plug in interceptors using the built-in AddDbContext and AddInterceptors methods:
services.AddDbContext<IApplicationDbContext, ApplicationDbContext>(
    (sp, options) => options
        .UseSqlServer(connectionString)
        .AddInterceptors(
            sp.GetRequiredService<UpdateAuditableInterceptor>(),
            sp.GetRequiredService<InsertOutboxMessagesInterceptor>()));But what if you're working with a legacy .NET Framework 4.x project using Entity Framework 6.x?
In this post, I’ll show you how to implement similar behavior—using interceptors, dependency injection, and auditable logic—in a .NET Framework + EF6 setup.
Background: Interceptors in EF6 vs EF Core
FeatureEF Core (Modern)EF6 (in .NET Framework)
Interceptor support | AddInterceptors() (per context) | DbInterception.Add() (global)
Dependency Injection | Built-in with IServiceCollection | Manual or via DI container
SaveChanges Auditing | Supported via SaveChanges() | Supported via SaveChanges()
Goal
Recreate this EF Core-style setup in EF6:
services.AddSingleton<UpdateAuditableInterceptor>(); services.AddSingleton<InsertOutboxMessagesInterceptor>(); services.AddDbContext<IApplicationDbContext, ApplicationDbContext>( (sp, options) => options .UseSqlServer(connectionString) .AddInterceptors(...));
Step 1: Define EF6 Interceptors
In EF6, you can create interceptors by implementing interfaces like IDbCommandInterceptor.
public class UpdateAuditableInterceptor : IDbCommandInterceptor { public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { // Add your auditing logic here } // You can leave the rest of the methods empty if not needed public void NonQueryExecuting(...) { } public void ScalarExecuting(...) { } // etc. }
You can do the same for your InsertOutboxMessagesInterceptor.
Step 2: Register Interceptors with EF6
EF6 does not support context-scoped interceptors. Instead, use the static DbInterception class:
Option A: Global Registration in Global.asax.cs
protected void Application_Start() { DbInterception.Add(new UpdateAuditableInterceptor()); DbInterception.Add(new InsertOutboxMessagesInterceptor()); }
Option B: Register via Dependency Injection
If you’re using Microsoft.Extensions.DependencyInjection (or any DI container), you can do this:
var services = new ServiceCollection(); services.AddSingleton<UpdateAuditableInterceptor>(); services.AddSingleton<InsertOutboxMessagesInterceptor>(); services.AddScoped<ApplicationDbContext>(provider => { var interceptor1 = provider.GetRequiredService<UpdateAuditableInterceptor>(); var interceptor2 = provider.GetRequiredService<InsertOutboxMessagesInterceptor>(); DbInterception.Add(interceptor1); DbInterception.Add(interceptor2); return new ApplicationDbContext("YourConnectionString"); });
⚠️ Important: EF6 interceptors are global and affect all DbContext instances in the AppDomain.
Bonus: SaveChanges Auditing Without Interceptors
For simple audit tracking like setting UpdatedAt timestamps, it's better to override SaveChanges() in your context:
public override int SaveChanges() { var entries = ChangeTracker.Entries<IAuditableEntity>(); foreach (var entry in entries) { if (entry.State == EntityState.Modified) { entry.Entity.UpdatedAt = DateTime.Now; } } return base.SaveChanges(); }
This is cleaner, more predictable, and avoids EF6 interceptor quirks.
Testing It All
Make sure to test with:
- Inserts and updates for auditable entities
 - Scenarios where interceptors may not fire (e.g., raw SQL)
 - Multi-threaded contexts if using global interceptors
 
Summary
You can mimic EF Core's AddInterceptors() in EF6, but with some caveats:
- Interceptors are global in EF6.
 - Manual DI setup is required.
 - For auditing, overriding SaveChanges() is often simpler and safer.
 
If you're maintaining a .NET Framework project but want to adopt some clean patterns from EF Core, this approach will help bridge that gap.
Please login to leave a comment.