Я уже рассматривал эту проблему в прошлом, и откатить собственное решение гораздо сложнее, чем вы думали вначале, в основном потому, что действительно трудно изменить способ, которым операторы Include
загружают связанные сущности (EF не ' действительно позволяет вам их фильтровать).
Но есть библиотека, которая может сделать это за вас.
Фильтрация результатов чтения
Это можно сделать довольно легко с помощью EntityFramework.DynamicFilters библиотека. (Я никоим образом не связан с разработчиками, мне просто очень нравится их библиотека)
Основной файл readme на самом деле имеет пример, который подходит для вашего случая использования:
modelBuilder.Filter("IsDeleted", (ISoftDelete d) => d.IsDeleted, false);
По сути, это будет возвращать только результаты Where(d => !d.IsDeleted)
, что именно то, что вы хотели бы. Этот фильтр применяется ко всем прямым выборкам и include, что означает, что эти мягко удаленные сущности, по сути, не существуют в том, что касается вашего домена.
Это предполагает, что ваши сущности все они происходят от общего root, имеющего флаг удаления, что я бы посоветовал вам сделать в любом случае.
Мягкое удаление объектов
Также возможно преобразовать жесткие удаления в мягкие удаления в контексте вашей базы данных, что означает, что вам не нужно переписывать код удаления, чтобы вместо этого обновить сущность (которая может быть громоздкой перепиской, и всегда возможно, что кто-то забудет ее здесь и там).
Вы можете переопределить поведение SaveChanges
(и SaveChangesAsync
) в своем классе контекста. Это позволяет вам найти все сущности, которые будут удалены, и дает вам возможность преобразовать это в оператор обновления, одновременно поднимая флаг IsDeleted
.
Это также гарантирует, что никто не сможет забыть для мягкого удаления. Ваши разработчики могут просто жестко удалить объекты (при обработке кода), и контекст преобразует их для них.
public class MyContext : DbContext
{
public override int SaveChanges()
{
ConvertHardDeleteToSoftDelete();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
ConvertHardDeleteToSoftDelete();
return await base.SaveChangesAsync(cancellationToken);
}
private void ConvertHardDeleteToSoftDelete()
{
var deletedEntries = ChangeTracker
.Entries<ISoftDelete>()
.Where(entry => entry.State == EntityState.Deleted)
.ToList();
foreach (var entry in deletedEntries)
{
entry.State = EntityState.Modified;
entry.IsDeleted = true;
}
}
}
В сочетании с предложенным выше фильтром динамического c это означает, что такой программный удаленный объект больше не будет отображаться в вашем приложении, но он все еще будет существовать в базе данных.