When users are deleting a lot of records as part of normal operations - they can and do make mistakes. Instead of the painful process of having to go to a backup to get these records, why not simply flag the records as IsDeleted?
ISoftDeleteEntity InterfaceImplement an interface IsSoftDeleteEntity with a boolean property IsDeleted, Entities requiring soft delete should implement this interface.
public interface ISoftDeleteEntity{bool IsDeleted { get; set; }}
Apply global query filters to automatically exclude soft-deleted entities:
modelBuilder.Entity<MyEntity>().HasQueryFilter(e => !e.IsDeleted);
This ensures queries do not return entities marked as deleted automatically eliminating the need to add an extra where condition in the actual queries.
Override the default delete behavior using EF Core interceptors by using an interceptor. This changes entity state to Modified and sets IsDeleted to true instead of completely removing the record.
public class SoftDeleteInterceptor : SaveChangesInterceptor{public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result){foreach (var entry in eventData.Context.ChangeTracker.Entries<ISoftDeleteEntity>()){if (entry.State == EntityState.Deleted){entry.Entity.IsDeleted = true;entry.State = EntityState.Modified;}}return base.SavingChanges(eventData, result);}}
Note: Make sure the entites that require soft delete has implemented the ISoftDeleteEntity interface for them to be captured into this interceptor.
Register the custom interceptor in the DbContext configuration:
services.AddDbContext<MyDbContext>(options =>options.UseSqlServer(connectionString).AddInterceptors(new SoftDeleteInterceptor()));
This integrates the interceptor with the EF Core context, this will ensure to run the entity through this interceptor every time context.saveChanges() is triggered.
Also see Using Audit Tools for alternatives to this approach using 3rd party auditing tools.
Watch William Liebenberg's SpendOps talk for more details about why soft deletes are advantageous in Azure: