C# 5 min read

Handling Soft Deletes in EF Core

When working with business applications, permanently deleting data is often undesirable. Records may need to be restored, audited or retained for compliance purposes. Soft deletes provide a simple solution by marking records as deleted rather than removing them from the database. In this article, we'll explore how to implement soft deletes in Entity Framework Core using query filters and save interception techniques.

Admin
Admin
.NET & IoT Developer
Handling Soft Deletes in EF Core

Introduction

Data deletion is a common requirement in business applications, but permanently removing records from a database can create challenges. Users may accidentally delete important information, administrators may need to review historical data, or regulations may require records to be retained for a period of time.

A soft delete strategy allows records to remain in the database while appearing deleted to the application. Rather than issuing a DELETE statement, a flag is updated to indicate that the record should no longer be treated as active.

Entity Framework Core provides several features that make implementing soft deletes straightforward and maintainable.

What Is a Soft Delete?

A soft delete occurs when a record is marked as deleted rather than physically removed from the database.

Consider a simple Customer table. Instead of deleting a row, an additional column such as IsDeleted is added.

IdNameEmailIsDeleted1John Smithjohn@example.comFalse2Jane Brownjane@example.comTrue

From the application's perspective, Jane Brown has been deleted. However, the data remains available should it ever need to be restored or audited.

This approach offers several advantages including improved data recovery, enhanced auditing capabilities and compliance with data retention requirements.

Creating a Soft Delete Base Entity

A common approach is to create a base class that contains the soft delete properties.

public abstract class BaseEntity
{
    public bool IsDeleted { get; set; }
    public DateTime? DeletedDate { get; set; }
}

Entities that support soft deletion can inherit from this class.

public class Customer : BaseEntity
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
}

This keeps the implementation consistent across multiple entity types.

Automatically Filtering Deleted Records

One of the most useful EF Core features for soft deletes is the global query filter.

Without a filter, every query would need to include an IsDeleted condition.

var customers = context.Customers
    .Where(c => !c.IsDeleted)
    .ToList();

Repeating this throughout an application quickly becomes tedious and increases the risk of mistakes.

A global query filter solves the problem.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Customer>()
        .HasQueryFilter(c => !c.IsDeleted);
}

Once configured, all queries automatically exclude deleted records.

var customers = context.Customers.ToList();

EF Core automatically applies the filter behind the scenes.

Converting Deletes into Updates

The next step is preventing EF Core from physically deleting records.

This can be achieved by overriding SaveChanges.

public override int SaveChanges()
{
    foreach (var entry in ChangeTracker.Entries<BaseEntity>())
    {
        if (entry.State == EntityState.Deleted)
        {
            entry.State = EntityState.Modified;
            entry.Entity.IsDeleted = true;
            entry.Entity.DeletedDate = DateTime.UtcNow;
        }
    }

    return base.SaveChanges();
}

Now when code attempts to remove an entity, EF Core updates the record instead.

var customer = context.Customers.Find(1);

context.Customers.Remove(customer);

context.SaveChanges();

Rather than generating a DELETE statement, EF Core issues an UPDATE statement that sets the IsDeleted flag.

Viewing Deleted Records

There may be occasions when administrators need to access deleted data.

EF Core allows query filters to be ignored.

var deletedCustomers = context.Customers
    .IgnoreQueryFilters()
    .Where(c => c.IsDeleted)
    .ToList();

This provides full access to both active and deleted records.

The feature is particularly useful when building administration screens or recovery tools.

Restoring Soft Deleted Records

One of the major benefits of soft deletes is the ability to restore data.

var customer = context.Customers
    .IgnoreQueryFilters()
    .First(c => c.Id == 1);

customer.IsDeleted = false;
customer.DeletedDate = null;

context.SaveChanges();

The record immediately becomes visible to normal queries again.

This functionality can save considerable time and effort compared with recovering data from backups.

Using SaveChanges Interceptors

Modern versions of EF Core support SaveChanges interceptors, providing a cleaner approach than overriding SaveChanges.

An interceptor centralises the logic and keeps DbContext classes focused on data access concerns.

public class SoftDeleteInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData,
        InterceptionResult<int> result)
    {
        var context = eventData.Context;

        if (context != null)
        {
            foreach (var entry in context.ChangeTracker
                .Entries<BaseEntity>())
            {
                if (entry.State == EntityState.Deleted)
                {
                    entry.State = EntityState.Modified;
                    entry.Entity.IsDeleted = true;
                    entry.Entity.DeletedDate = DateTime.UtcNow;
                }
            }
        }

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

The interceptor can then be registered when configuring the DbContext.

This approach is particularly useful in larger applications where multiple contexts share common behaviour.

Considerations and Best Practices

Soft deletes are powerful, but they are not always the correct solution. Tables that grow rapidly may accumulate large volumes of deleted data over time, potentially impacting query performance and storage requirements.

It is also important to ensure that unique constraints are designed carefully. For example, if an email address must be unique, a soft deleted record could prevent a new user from registering with the same address.

Regular archiving and data retention strategies should be considered alongside soft delete implementations to maintain optimal database performance.

Conclusion

Soft deletes provide a practical way to preserve historical data while preventing users from seeing records that should no longer be active. Entity Framework Core makes implementation straightforward through global query filters, SaveChanges overrides and interceptors.

By adopting a soft delete strategy, applications gain improved auditability, recovery options and data retention capabilities without significantly increasing complexity. For many business systems, it is a safer and more flexible alternative to permanently deleting records from the database.

Share:

Become a member

Get the latest news right in your inbox. It's free and you can unsubscribe at any time. We hate spam as much as we do, so we never spam!

Read next

Tracking vs No-Tracking Queries Explained

When working with Entity Framework, one of the most overlooked performance considerations is whether queries should be tracked or not. By default, Entity Framework tracks entities returned from queries, allowing changes to be detected and saved automatically. However, tracking comes with overhead and is not always necessary. Understanding when to use tracking and no-tracking queries can improve application performance and reduce memory usage.

Admin 02-Jun-2026

The Bedroom Coder — retro computers, modern .NET, and late-night experiments.

Navigation

Contact

Want to talk retro tech or modern coding? I'd love to hear your thoughts.

© 2026 The Bedroom Coder. All rights reserved.