What is soft delete?
- Prevents permanent deletion of records. 
- Flag set on record, indicating "delete". 
- Allows for easy restoration and maintains data integrity. 
How to implement:
- Create Marker Interface.
Define a marker interface ISoftDelete to mark entities that support soft delete.
public interface ISoftDelete
{
    bool IsDeleted { get; set; }
    DateTime? DeleteOnUtc { get; set; }
}
- Implement Soft Delete Interceptor.
Create a class SoftDeleteInterceptor to handle soft deletes in EF Core.
public sealed class SoftDeleteInterceptor: SaveChangesInterceptor
{
    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
        DbContextEventData eventData,
        InterceptionResult<int> result,
        CancellationToken cancellationToken = default)
    {
        if (eventData.Context is null)
        {
            return base.SavingChangesAsync(eventData, result, cancellationToken);
        }
        IEnumerable<EntityEntry<ISoftDeletable>> entries =
            eventData.Context.ChangeTracker.Entries<ISoftDeletable>()
                .Where(e => e.State == EntityState.Deleted);
        foreach (EntityEntry<ISoftDeletable> softDeletable in entries)
        {
            softDeletable.State = EntityState.Modified;
            softDeletable.Entity.IsDeleted = true;
            softDeletable.Entity.DeletedOnUtc = DateTime.UtcNow;
        }
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }
}
- Configure Application Context.
Configure EF Core to use the soft delete interceptor and global query filters.
public class ApplicationDbContext: DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
    public DbSet<User> Users { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        // HasQueryFilter is used to filter soft-deleted data automatically.
        modelBuilder.Entity<User>().HasQueryFilter(r => !r.IsDeleted);
        modelBuilder.Entity<User>()
        .HasIndex(r=> r.IsDeleted)
        .HasFilter("IsDeleted=0");
        // This HasFilter method accepts the SQL filter for records that will be 
            included in the index.
        // You can also create a filtered index using SQL:
         /*
           Create index IX_Users_IsDeleted on User(IsDeleted) where IsDelete =0;
         */
    }
}
- Register SoftDeleteIntercepter with dependency injection.
services.AddSingleton<SoftDeleteInterceptor>();
services.AddDbContext<ApplicationDbContext>(
    sp, options)=>options
        .UseSqlServer(connectionString)
        .AddInterceptor(
               sp.GetRequiredService<SoftDeleteInterceptor>()));
- Example Entity.
Create an example entity Review implementing the soft delete marker interface.
public class User: ISoftDeletable
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public bool IsDeleted { get; set; }
    public DateTime? DeletedOnUtc { get; set; }
}
Benefits:
- Flexibility in managing data.
- Simplified queries with automatic exclusion of soft-deleted records.
- Improved performance with filtered indexes.
Conclusion
- Soft delete ensures data integrity and flexibility in managing records without compromising performance.
 


 
    
Top comments (0)