【问题标题】:Entity Framework IsDeleted or IsDisabled column实体框架 IsDeleted 或 IsDisabled 列
【发布时间】:2015-08-12 18:36:12
【问题描述】:

我不想从我的某些表中删除行,所以我添加了一个 IsDeleted 列。

代表这些表的 POCS 都实现了 IDeleteable 接口。

当我做类似的事情时 -

context.Users.Where(u => u.Firstname == "Dave")

我想只取回没有设置 IsDeleted 位的行。

我不想将&& IsDeleted == false 添加到Where

我还希望此解决方案适用于 SingleFirst 或仅适用于 context.Users

我想有类似question 的解决方案。

【问题讨论】:

标签: c# sql-server entity-framework


【解决方案1】:

鉴于下面 trailmax 的评论,是的,下面的原始答案确实将所有数据加载到内存中,因为它暴露了链式枚举。

尝试将IQueryable 调用链接在一起时会出现两个问题。

  1. EF 不知道如何翻译生成的.Invoke() 调用
  2. 如果您尝试维护像 .Where() 这样的命名约定,您最终会选择错误的扩展名,因为 IDeletable 只公开了一个可以在其上构建谓词的列。

以下依赖于 LinqKit 库来链接表达式,语法不再流畅,但它确实避免了任何立即执行。

var users = context.Users.WhereIsNotDeleted(x => x.Id > 0).ToList();

public static class Extension
{
    public static IEnumerable<T> WhereIsNotDeleted<T>(this IQueryable<T> source,
        Expression<Func<T, bool>> predicate) where T : IDeletable
    {
        var query = source.AsExpandable().Where(x => !x.IsDeleted);
        return query.Where(predicate);
    }
}

假设IDeletable 保证IsDeleted 存在,您可以创建一个始终为您执行此检查的扩展方法。

public interface IDeletable
{
    bool IsDeleted { get; set; }
}

public static class Extension
{
    public static IEnumerable<T> WhereNotDeleted<T>(this IEnumerable<T> source, 
        Func<T, bool> predicate) where T : IDeletable
    {
        return source.Where(x => !x.IsDeleted).Where(predicate);
    }
}

在下面的简单测试中,仅返回两条记录,因为第三条记录已被软删除。

void Main()
{
    var x = new List<Test>();

    x.Add(new Test{ Number = "one" });
    x.Add(new Test{ Number = "two" });
    x.Add(new Test{ Number = "three", IsDeleted = true });

    var y = x.WhereNotDeleted(a => a != null);
    y.Count().Dump();
}

public class Test : IDeletable
{
    public string Number { get; set; }
    public bool IsDeleted { get; set; }
}

根据您的评论,如果您希望它与 EXISTING LINQ 扩展方法一起使用,您实际上无法做到,因为您会遇到调用歧义。

如果您想调用 .Where() 并将其转换为您的条件,仅为实现 IDeletable 的对象,您需要包装每个 LINQ 扩展方法。

public static class Extension
{
    public static IEnumerable<IDeletable> Where(this IEnumerable<IDeletable> source, 
        Func<IDeletable, bool> predicate)
    {
        return System.Linq.Enumerable.Where(source, (x => predicate(x) && !x.IsDeleted));
    }
}

在这些扩展方法中,您必须通过实例调用而不是作为扩展方法调用基本 LINQ 方法,否则您将遇到堆栈溢出异常。

【讨论】:

  • 我知道我没有说出来,但我希望它不仅仅适用于 Where。 Single、FirstOrDefault 等
  • 不能。它们是扩展方法,不能被覆盖,否则你会收到一个模棱两可的调用错误。
  • 每次在 LINQ 中看到IEnumerable 在 EF 旁边,我都闻到了麻烦。我认为这可能最终会在内存中进行过滤而不是在数据库中进行过滤。在大型场景中,它可能对性能非常不利。虽然我没有尝试/确认这一点,但这是需要注意的。
  • 只需运行一个小测试,我就可以确认这种过滤方式非常低效 - EF 执行Select * from tableName,然后在内存中执行过滤。这对于小型数据集来说很好,但在较大数据集上的性能和内存消耗非常糟糕。所以我不建议在生产系统中使用这种方式进行过滤。
  • @trailmax 你是绝对正确的,我已经用一个有效的替代方法更新了我的答案。不幸的是,最初的答案是为了解决 OP 对轻松重用.Where() 的要求,但显然这会导致更大的问题。
【解决方案2】:

您可以为每个将 where 子句用作过滤器的表创建一个视图。然后,您可以从实体框架而不是基础表访问该视图,而不必担心。您的用户永远不知道基础表,只知道视图。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多