Entity framework core – Interceptors

by admin
keyboard and wooden delete piece

Interceptors allow developers to inject custom logic into various points of the EF Core pipeline, enabling them to inspect and modify the behavior of EF Core operations. In simpler terms, interceptors act as hooks that intercept (or intercept and modify) database operations and events before they are executed or after they are executed but before they are materialized into entities. They provide a way to extend and customize the behavior of EF Core without modifying the core framework code.

Interceptors can be used for various purposes, such as:

  1. Logging
    Interceptors can be used to log the database operations being executed by EF Core. This helps in debugging and understanding the underlying SQL queries sent to the database.
  2. Caching
    Interceptors can be employed to implement caching mechanisms for frequently accessed data, improving application performance and reducing database round-trips.
  3. Auditing and Security
    Interceptors can capture information about changes made to entities, user actions, or security-related events to implement auditing or access control features.
  4. Soft deletes
    Interceptors can implement soft delete functionality by intercepting delete operations and marking entities as “soft deleted” instead of physically deleting them from the database.
  5. Validation
    By Intercepting SaveChanges or SaveChangesAsync we can allow custom validation of entities before they are persisted to the database, enforcing business rules and data integrity.

Let’s take a look at implement number 4 (soft deletes). In this example we will intercept deletes and set an IsDeleted boolean and the DateTime that the entity was (soft)deleted.

Step 1 – Add the ISoftDelete interface

public interface ISoftDelete
{
    public DateTime? DeletionTime { get; set; }
    public bool IsDeleted { get; set; }
}

Step 2 – Creating the interceptor

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

public class SoftDeleteInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
    {
        if (eventData.Context != null)
        {
            foreach (var entry in eventData.Context!.ChangeTracker.Entries<ISoftDelete>())
            {
                if (entry.State == EntityState.Deleted)
                {
                    entry.State = EntityState.Modified;
                    entry.Entity.IsDeleted = true;
                    entry.Entity.DeletionTime = DateTime.UtcNow;
                }
            }
        }

        return base.SavingChanges(eventData, result);
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        if (eventData.Context != null)
        {
            foreach (var entry in eventData.Context!.ChangeTracker.Entries<ISoftDelete>())
            {
                if (entry.State == EntityState.Deleted)
                {
                    entry.State = EntityState.Modified;
                    entry.Entity.IsDeleted = true;
                    entry.Entity.DeletionTime = DateTime.UtcNow;
                }
            }
        }

        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }
}

This interceptor modifies the SaveChanges and SaveChangesAsync method. Before saving, the code checks if the entity implements our ISoftDelete interface and if the EntityState is Deleted. If both conditions are met, it changes the entity state to ‘Modified’, sets the IsDeleted property to true, and updates the Deleted property with the current UTC time.

Step 3 Configure the interceptor

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.AddInterceptors(new SoftDeleteInterceptor());
    }

Add the code above in your DbContext.

That’s all there is to it. This is an example of a simple interceptor, in the real world you would probably also add the user that deleted the entity or any other info.

Learn how to implement a global query filter, take a look at:

https://docubear.com/entity-framework-core-global-query-filters

Related Posts

Leave a Comment