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)