原理
回收站是当用户删除一条记录时,不是直接从数据库中删除,而是将其放入“回收站”,以便用户可以在需要时恢复数据。
在Abp框架中,若实体实现了ISoftDelete,则将实体标记为删除时不是物理删除,而是“软删除”
1 2 3 4 5 6 7
| public interface ISoftDelete { /// <summary> /// Used to mark an Entity as 'Deleted'. /// </summary> bool IsDeleted { get; set; } }
|
当使用仓储删除一条记录时,ABP会自动将 IsDeleted 设置为true,并将删除操作替换为修改操作。 在查询数据库时会自动过滤软删除的实体。
利用这个原理,可以将“软删除”行为认为是放入了“回收站”,而将“恢复”行为认为是从“回收站”中取出。将记录硬删除的行为认为是“永久删除”, 将全部已“软删除”的记录硬删除,则是“清空回收站”
因此我需要实现一个自定义过滤器,用于查看已删除的实体。
创建过滤器
定义仅查看软删除的过滤器,命名”OnlyShowSoftDelete”
1 2 3 4 5
| public class FileDataFilters { public const string OnlyShowSoftDelete = "OnlyShowSoftDelete";
}
|
在模块中注册过滤器,isEnabledByDefault参数为false,默认不启用
1
| Configuration.UnitOfWork.RegisterFilter(FileDataFilters.OnlyShowSoftDelete, false);
|
在DbContext中,增加IsOnlyShowSoftDeleteFilterEnabled属性,用于判断当前的查询上下文中是否启用了“仅查看软删除”过滤器
1 2
| public bool IsOnlyShowSoftDeleteFilterEnabled => CurrentUnitOfWorkProvider?.Current?.IsFilterEnabled(FileStorage.Uow.FileDataFilters.OnlyShowSoftDelete) == true;
|
在DbContext中,重写CreateFilterExpression方法,当启用了“仅查看软删除”过滤器时,自动过滤软删除的实体
1 2 3 4 5 6 7 8 9 10 11 12
| protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>() { var expression = base.CreateFilterExpression<TEntity>(); if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { Expression<Func<TEntity, bool>> softDeleteFilter = e => !IsOnlyShowSoftDeleteFilterEnabled || ((ISoftDelete)e).IsDeleted; expression = expression == null ? softDeleteFilter : CombineExpressions(expression, softDeleteFilter); }
return expression; }
|
使用过滤器
查询
查询正常业务时,不需要对默认的过滤器做操作。
在查看“回收站”中的数据时,需要关闭AbpDataFilters.SoftDelete过滤器,打开FileDataFilters.OnlyShowSoftDelete过滤器。
在使用仓储做任何查询(如: GetAll或Get)之前,加入以下代码:
1 2
| UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete); UnitOfWorkManager.Current.EnableFilter(FileDataFilters.OnlyShowSoftDelete);
|
删除
在删除记录时,通过调用仓储的Delete()方法,将记录放入“回收站”中。调用HardDelete()方法,将记录永久删除。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public virtual async Task DeleteAsync(File file, bool isHardDelete = false) { if (isHardDelete) { await _repository.HardDeleteAsync(file); //永久删除 } else { await _repository.DeleteAsync(file); //放入“回收站” } }
|
恢复
获取已经“软删除”的记录,调用UnDelete()方法,将记录从“回收站”中取出
1 2 3 4 5 6
| UnitOfWorkManager.Current.DisableFilter(AbpDataFilters.SoftDelete); UnitOfWorkManager.Current.EnableFilter(FileDataFilters.OnlyShowSoftDelete);
var currentFile = await _repository.GetAsync(file.Id); currentFile.UnDelete();
|
新版Volo.Abp的控制器配置
新版本的Volo.Abp,取消了字符串配置方式,需要为控制器指定一个接口。
创建IOnlyShowSoftDelete接口:
1 2 3
| public interface IOnlyShowSoftDelete { }
|
添加配置,将默认的控制器状态设置为不启用
1 2 3 4
| Configure<AbpDataFilterOptions>(options => { options.DefaultStates[typeof(IOnlyShowSoftDelete)] = new DataFilterState(isEnabled: false); });
|
同样,在DbContext中,重写CreateFilterExpression方法,当启用了“仅查看软删除”过滤器时,自动过滤软删除的实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| protected bool IsOnlyShowSoftDeleteFilterEnabled => DataFilter?.IsEnabled<IOnlyShowSoftDelete>() ?? false;
protected override Expression<Func<TEntity, bool>> CreateFilterExpression<TEntity>() { var expression = base.CreateFilterExpression<TEntity>();
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { Expression<Func<TEntity, bool>> softDeleteFilter = e => !IsOnlyShowSoftDeleteFilterEnabled || ((ISoftDelete)e).IsDeleted; expression = expression == null ? softDeleteFilter : CombineExpressions(expression, softDeleteFilter); }
return expression; }
|
注入 IDataFilter 服务到你的类中
1 2 3 4 5 6 7
| private readonly IDataFilter _dataFilter;
public MyBookService(IDataFilter dataFilter) { _dataFilter = dataFilter; }
|
使用_dataFilter的Enable和Disable方法,启用或禁用过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public async Task<List<Book>> GetAllBooksAsync() { if(input.IncludeTrash) { //临时显示软删除的记录 using ( _dataFilter.Disable<ISoftDelete>()) using ( _dataFilter.Enable<IOnlyShowSoftDelete>()) { return await _bookRepository.GetListAsync(); } } else { return await _bookRepository.GetListAsync(); } }
|
或使用UnitOfWorkManager解析IDataFilter服务
1 2 3 4 5
| using (input.IncludeTrash ? this.UnitOfWorkManager.Current.ServiceProvider.GetRequiredService<IDataFilter<ISoftDelete>>().Disable() : NullDisposable.Instance) using (input.IncludeTrash ? this.UnitOfWorkManager.Current.ServiceProvider.GetRequiredService<IDataFilter<IOnlyShowSoftDelete>>().Enable() : NullDisposable.Instance) { return await _bookRepository.GetListAsync(); }
|