Table of Contents

Soft Delete

Soft delete allows entities to be logically removed without deleting the database row. Deleted entities are hidden from queries by default and can be restored or audited later.

Overview

Implement ISoftDeletable on any entity to opt in to soft-delete behavior:

public class Article : ISoftDeletable
{
    public Guid Id { get; set; }
    public string Title { get; set; } = "";
    public bool IsDeleted { get; set; }
    public DateTime? DeletedAt { get; set; }
}

When a DELETE /articles/{id} request is received:

  • MapCrud sets IsDeleted = true and DeletedAt = DateTime.UtcNow
  • No row is removed from the database
  • The operation is idempotent: deleting an already-soft-deleted entity returns 204 No Content

Filtering

Soft-deleted entities are automatically excluded from GET responses unless the client requests them:

GET /articles                        # returns only non-deleted articles
GET /articles?includeDeleted=true    # returns all articles including soft-deleted
GET /articles/{id}                   # returns 404 if the article is soft-deleted
GET /articles/{id}?includeDeleted=true  # returns the article even if soft-deleted

EF Core Setup

Call ApplySoftDeleteFilters() in OnModelCreating to register global query filters automatically for all ISoftDeletable entities:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplySoftDeleteFilters();
    // ...
}

This registers an EF Core global query filter WHERE IsDeleted = 0 on every entity type that implements ISoftDeletable. The filter is bypassed when IgnoreQueryFilters() is applied (which happens automatically inside EfRepository when includeDeleted=true).

In-Memory Setup

The InMemoryRepository applies soft-delete filtering in-process. No additional configuration is needed.

Hard Delete

For entities that do not implement ISoftDeletable, the DELETE endpoint performs a hard delete (removes the row/entry permanently).