Implementing Soft Deletes in a .NET Core App with Entity Framework

Soft Deletes is a concept in Software Development that means records are not completely deleted. These records are marked as deleted but are still available in the table. This is achieved by setting a flag or column in the table namely IsDeleted = true. Softdeletes can come in handy if you need to keep the records even though these were deleted from the Application.

Step 1: Start by adding an IsDeleted field in the Model class.

For the purpose of this demo, we will work with the User Model class.

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    /**
     * This property will be used to determine
     * if a record is deleted or not
     */
    public bool IsDeleted { get; set; } = false;
}

Step 2: Apply Global query filter to the DbContext class to exclude

deleted records.

When querying the database tables, you can apply a global query filter in the OnModelCreating method of your DbContext class to exclude these so called SoftDeleted entries from showing up in the results.

This query filter essentially works like a where SQL clause.

public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //Global filter applied to only fetch items that are not deleted
        //from softdelete tables
        modelBuilder.Entity<User>().HasQueryFilter(user=>!user.IsDeleted);
    }
}

Step 3: SoftDelete a record

To SoftDelete a user just set the IsDeleted property to true and save the changes to the database.

//delete a user
public async Task DeleteUser(int id)
{
    var user = await context.Users.FindAsync(id);
    if (user != null)
    {
        //soft delete the user here
        user.IsDeleted = true;
        await context.SaveChangesAsync();
    }
}

Step 4: Retrieve records as you would normally

To retrieve users that are not deleted, simply query the database as usual and under the hood, the global query filter will be applied to only return records that are not deleted.

//get all users
public async Task<List<User>> GetUsers()
{
    return await context.Users.ToListAsync();
}

Step 5: Retrieve deleted users only

To retrieve all deleted users, simply chain the .IgnoreQueryFilters() method to the context.Users with the IsDeleted condition in the where statement.

//get deleted users
public async Task<List<User>> GetDeletedUsers()
{
    return await context.Users.IgnoreQueryFilters()
        .Where(user => user.IsDeleted).ToListAsync();
}

And that is all you need to implement SoftDeletes using EFCore. SoftDeletes can be useful if your use case requires to retain records perhaps for auditing purposes, but just bear in mind that if there are huge volumes of records being written and deleted from the database, overtime it may increase the size of the database.

Happy coding!