有没有办法防止 Entity Framework 从不读取 属性 是特定值的条目?
Is there a way to prevent Entity Framework to NEVER read entries where a property is a specific value?
我有一个软件已经使用了一段时间,今天我们的客户决定我们不删除任何数据,而是隐藏它们。为此,我计划向所有表添加一个 "isDeleted" 属性 并更改所有删除方法以将此 属性 设置为 "true"。
问题是,我的阅读量是删除量的 1000 倍,我可以拥有一个用户并尝试使用实体关系读取该用户的所有评论,我必须为每个评论添加一个 "Where(x => !x.isDeleted)"像这样阅读,或者如果可能的话,选择不读取所有 isDeleted 为 true 的数据。
后者有可能吗?如果没有,是否可以替代写入 "Where(x => !x.isDeleted)" 一千次?
我过去曾研究过这个问题,推出自己的解决方案比您最初想象的要困难得多,主要是因为很难改变 Include
语句加载相关实体的方式(EF 确实不允许您过滤它们)。
但是有一个图书馆可以为您完成。
过滤读取结果
使用 EntityFramework.DynamicFilters 库可以很容易地完成。 (我与开发者没有任何关系,我真的很喜欢他们的图书馆)
主要自述文件实际上有一个适合您的用例的示例:
modelBuilder.Filter("IsDeleted", (ISoftDelete d) => d.IsDeleted, false);
基本上,它只会 return 结果 Where(d => !d.IsDeleted)
,这正是您想要的。此过滤器适用于所有直接提取 和 包含语句,这意味着就您的域而言,这些软删除实体基本上不存在。
这确实假定您的实体都派生自具有删除标志的共享根,无论如何我都建议您这样做。
软删除实体
也可以在您的数据库上下文中将硬删除转换为软删除,这意味着您不需要重写删除代码来更新实体(这可能是一个麻烦的重写,而且它总是可能有人忘记了它)。
您可以在您的上下文 class 中覆盖 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;
}
}
}
结合上面的动态过滤建议,这意味着这样的软删除实体不会再次出现在您的应用程序中,但它仍然存在于数据库中。
我有一个软件已经使用了一段时间,今天我们的客户决定我们不删除任何数据,而是隐藏它们。为此,我计划向所有表添加一个 "isDeleted" 属性 并更改所有删除方法以将此 属性 设置为 "true"。
问题是,我的阅读量是删除量的 1000 倍,我可以拥有一个用户并尝试使用实体关系读取该用户的所有评论,我必须为每个评论添加一个 "Where(x => !x.isDeleted)"像这样阅读,或者如果可能的话,选择不读取所有 isDeleted 为 true 的数据。
后者有可能吗?如果没有,是否可以替代写入 "Where(x => !x.isDeleted)" 一千次?
我过去曾研究过这个问题,推出自己的解决方案比您最初想象的要困难得多,主要是因为很难改变 Include
语句加载相关实体的方式(EF 确实不允许您过滤它们)。
但是有一个图书馆可以为您完成。
过滤读取结果
使用 EntityFramework.DynamicFilters 库可以很容易地完成。 (我与开发者没有任何关系,我真的很喜欢他们的图书馆)
主要自述文件实际上有一个适合您的用例的示例:
modelBuilder.Filter("IsDeleted", (ISoftDelete d) => d.IsDeleted, false);
基本上,它只会 return 结果 Where(d => !d.IsDeleted)
,这正是您想要的。此过滤器适用于所有直接提取 和 包含语句,这意味着就您的域而言,这些软删除实体基本上不存在。
这确实假定您的实体都派生自具有删除标志的共享根,无论如何我都建议您这样做。
软删除实体
也可以在您的数据库上下文中将硬删除转换为软删除,这意味着您不需要重写删除代码来更新实体(这可能是一个麻烦的重写,而且它总是可能有人忘记了它)。
您可以在您的上下文 class 中覆盖 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;
}
}
}
结合上面的动态过滤建议,这意味着这样的软删除实体不会再次出现在您的应用程序中,但它仍然存在于数据库中。