Stop Leaking Data: Soft Deletes with EF Core Global Filters

September 15, 2025 · 7 min

Three months into a new multi-tenant SaaS project, we shipped a simple delete feature. Hours later, our support channels lit up. Customers were confused—they’d delete an invoice, but it would mysteriously reappear in their quarterly reports.

The bug was a classic. A single, forgotten Where clause. A reporting query, written by someone new to the domain, didn’t include our manual !IsDeleted filter. Boom. Soft-deleted records were leaking straight into the UI.

That incident taught me a hard lesson: manual soft deletes are a ticking time bomb. EF Core’s global query filters are the only way I’ll implement them now. They’re not just a convenience; they’re a safety net.

The ‘Just Add an IsDeleted Flag’ Trap

Everyone starts here. You add an IsDeleted property to your entity and tell the team, “Just remember to filter it out.” It works, for a while.

// This works... until it doesn't.
var activeUsers = context.Users
    .Where(u => !u.IsDeleted)
    .ToList();

// Oops. A developer writing a new report forgets the filter.
var userReport = context.Users
    .Include(u => u.Orders)
    .ToList(); // Deleted users are back from the dead.

In a growing codebase with multiple developers, someone will forget the Where clause. It’s inevitable. In our case, it wasn’t just a weird bug; it broke customer trust. They thought our software was unreliable.

EF Core’s Global Query Filters to the Rescue

This is where you stop …

Read more