You can enable this on DbContext level. Here are a few extensions.
Extension for applying soft delete on save:
public interface ISoftDelete
{
public bool IsDeleted { get; }
}
public static class DbContextExtensions
{
public static void ApplySoftDelete(this DbContext dbContext)
{
var entries = dbContext.ChangeTracker.Entries<ISoftDelete>().Where(x => x.State == EntityState.Deleted);
foreach (var entry in entries)
{
entry.CurrentValues.SetValues(entry.OriginalValues);
entry.State = EntityState.Unchanged;
entry.CurrentValues[nameof(ISoftDelete.IsDeleted)] = true;
var referenceEntries = entry.References.Where(x => x.TargetEntry is not null &&
x.TargetEntry.Metadata.IsOwned());
foreach (var targetEntry in referenceEntries.Select(e => e.TargetEntry))
{
targetEntry!.CurrentValues.SetValues(targetEntry.OriginalValues);
targetEntry!.State = EntityState.Unchanged;
}
}
}
}
Then just override the SavechangesAsync
as follows
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
this.ApplySoftDelete();
return await base.SaveChangesAsync(cancellationToken);
}
Extension for applying SoftDelete filter
public static class ModelBuilderExtensions
{
public static void ConfigureSoftDelete(this ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(ISoftDelete).IsAssignableFrom(entityType.ClrType))
{
modelBuilder.Entity(entityType.Name, x => x.Property(nameof(ISoftDelete.IsDeleted)));
entityType.AddSoftDeleteQueryFilter();
}
}
}
private static void AddSoftDeleteQueryFilter(this IMutableEntityType entityData)
{
var methodToCall = typeof(ModelBuilderExtensions)
.GetMethod(nameof(GetSoftDeleteFilter), BindingFlags.NonPublic | BindingFlags.Static)!
.MakeGenericMethod(entityData.ClrType);
var filter = methodToCall.Invoke(null, Array.Empty<object>());
entityData.SetQueryFilter((LambdaExpression?)filter);
entityData.AddIndex(entityData.FindProperty(nameof(ISoftDelete.IsDeleted))!);
}
private static LambdaExpression GetSoftDeleteFilter<TEntity>() where TEntity : class, ISoftDelete
{
Expression<Func<TEntity, bool>> filter = x => !x.IsDeleted;
return filter;
}
}
Then override the OnModelCreating in DbContext and add this call.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ConfigureSoftDelete();
}