EntityFramework上软删除的优雅方式是什么?我已经将一个属性(数据库字段)标识为已删除,并始终在 linq 语句上使用此过滤器。
像
Foo Class
int NumberField
string Description
bool Deleted
contexts.Foos.Where(x=> !x.Deleted);
在复杂的查询上是不可行的。
任何帮助表示赞赏..
EntityFramework上软删除的优雅方式是什么?我已经将一个属性(数据库字段)标识为已删除,并始终在 linq 语句上使用此过滤器。
像
Foo Class
int NumberField
string Description
bool Deleted
contexts.Foos.Where(x=> !x.Deleted);
在复杂的查询上是不可行的。
任何帮助表示赞赏..
是的,这可以通过 EF 模式实现。如果您使用 Fascade/repository 模式并每次都通过该 fascade 访问。
例如,所有存储库类上的接口实现可能如下所示:
class MyRepositoryBase<T>....
public IQueryable<T> ValidQuerySet // this is not deleted check Set
{ get { return Context.Set<T>().Where(t => t.deleted != true);
}
}
您将完全像访问原始 DbSet 一样访问它。EF 将结合这些条件。
var myQuerySet = MyRespository<T>.ValidQuerySet.Where(t=>t.foo == "bar");
您可能需要考虑在上下文类本身上创建存储库,而不是直接查询您的上下文(如在此站点上找到)。然后,您可以做的是每当您查询记录时(例如通过使用Filter<T>(Expression<Func<T, bool>> predicate)
存储库的方法,您始终可以执行以下操作:
return Context.Set<Foo>().Where<Foo>(x => !x.Deleted).Where<Foo>(predicate).AsQueryable<Foo>();
更好的是,如果您要在多个对象类型中实现该软删除,然后将其拉入一个抽象类(SoftDeleteable
例如调用它),那么您的 Filter 方法签名可能是:
public virtual IQueryable<T> Filter<T>(Expression<Func<T, bool>> predicate) where T : SoftDeletable
您可能不会喜欢我的回答,但我能想到的“不那么痛苦”的方式需要大量存储过程:每个实体 1 个,而您所做的只是如果存在 UPDATE foo Deleted = True。我的通常看起来像这样
PROCEDURE [dbo].[SP_Address_UnSet]
@ID bigint
AS
BEGIN
DECLARE @IsInactive bit
SELECT @IsInactive = IsInactive
FROM [Address]
WHERE AddressID = @ID
IF (0 = @@ROWCOUNT)
RETURN -1
-- implicit else
IF (1 = @IsInactive)
RETURN 0
-- implicit else
UPDATE [Address]
SET IsInactive = 1
WHERE AddressID = @ID
RETURN @@ROWCOUNT -- should be 1
END
然后在每个实体上映射到该 SP。
您可以有 1 个单一的 SP,并将表名作为参数传递,但由于您不能通过动态查询直接在 T-SQL 中使用它,因此您必须构建子执行(并担心 SQL 注入!!)
Exec('SELECT * FROM ' + @tableName)
全局查询过滤器 (EF Core)和实体框架拦截器 (EF 6+)是在 EntityFramework 上添加的新功能,用于解决此类问题。
我发现以这种方式进行“软删除”非常痛苦:
我建议您将硬“已删除”条目放入不同的表或数据库中,从长远来看,它将为您节省时间。