在 EF Core 中设置预定义条件

Set predefined condition in EF Core

假设

// connection strings and other configurations is not mentioned for simplicity
MyDbContext context = new MyDbContext();
Var Entities = Context.Set< table1>();
Var list = Entities.ToList();

LINQ to SQL 生成以下 SQL 查询:

Select col0, col1, isdeleted, coln  
From table1

此代码是完美的 return table1 对象列表(假设 20 行)。

我的要求是:

// looking for a function or anything. This is my need
Entities.AddDefaultFilter("isdeleted", false);

// expected rows (18 rows, 2 rows have isdeleted = true) 
// Those should be excluded 
Var list = Entities.ToList();

我以非常糟糕的方式实现了这一点:

var list = Entities.ToList();

return list.Where(x => ((bool?)x.GetType().GetProperty("IsDeleted").GetValue(x)) == false).ToList(); 

这段代码是完美的 return 只有那些满足条件 isdeleted = false.

的行

这很糟糕,因为它首先从数据库加载所有行,然后过滤/删除具有 isdeleted = true 值的行。

如果 table1 有 1M 行和 300K 行 isdeleted = true 那么它需要额外的时间和内存。

抱歉语法错误。

谢谢。

与其先获取整个列表,不如先使用where谓词过滤结果再去服务器:

return Entities.Where(x => x.IsDeleted==false).ToList();

或更简单:

return Entities.Where(x => !x.IsDeleted).ToList();

这里的重点是语句末尾的 ToList()。然后它应该输出 SQL 如下:

Select col0,col1, isdeleted, coln from table1 WHERE isdeleted=0

我对 EF Core 不太熟悉,但如果它以与 EF6 类似的方式工作,那么当您在 DbSet 上调用 .ToList() 时,它将有效地执行相当于 SQL Select * 在 table/entity.

所以通过

var list = Entities.ToList();

您正在将该实体的所有记录返回到内存中。然后过滤内存数据:

return list.Where(x => ((bool?)x.GetType().GetProperty("IsDeleted").GetValue(x)) == false).ToList();

如果您要执行以下操作:

return Entities.Where(x => !x.IsDeleted).ToList()

生成的 SQL 将包含 where 子句并且效率更高。

2020 年 4 月 5 日更新:

要包含通用类型的过滤器,您可以构建一个表达式并将其传递到 LINQ 语句中:

var props = typeof(TEntity).GetProperties();

if (props.Any(p => p.Name == "IsDeleted"))
{
    ParameterExpression pe = Expression.Parameter(typeof(TEntity), "x");

    ConstantExpression valExpression = Expression.Constant(true, typeof(bool));
    MemberExpression member = Expression.Property(pe, "IsDeleted");
    Expression predicateBody = Expression.Equal(member, valExpression);

    var final = Expression.Lambda<Func<TEntity, bool>>(body: predicateBody, parameters: pe);

    return Entities.Where(final.Compile()).ToList();
}
else
{
   return Entities.ToList();
}

如果您的所有实体都包含 "IsDeleted" 属性,则可以删除检查它是否存在的检查。代码尚未经过测试,但应该是正确的。

这段代码是从@PeterG 的代码中复制过来的,并做了一些修改:

 var props = typeof(T).GetProperties();
                if (props.Any(p => p.Name == "IsDeleted"))
                {
                    ConstantExpression valExpression = Expression.Constant(false, typeof(bool));//true to false
                    ParameterExpression pe = Expression.Parameter(typeof(T), "x");//this line was moved
                    MemberExpression member = Expression.Property(pe, "IsDeleted");
                    Expression predicateBody = Expression.Equal(member, valExpression);
                    var final = Expression.Lambda<Func<T, bool>>(body: predicateBody, parameters: pe);                

                    //var iq =(IQueryable<T>) Entities.Where(final.Compile());
                    //var sql = EfExtensions.ToSql(iq);
                    var list2 = Entities.Where(final.Compile()).ToList();//this the main expectation.
                    return list2 ;
                }
                else
                {//I keep my old code  if i ever need.
                    var list= Entities.ToList();
                    return list.Where(x => ((bool?)x.GetType().GetProperty("IsDeleted").GetValue(x)) != true).ToList();
                }

所有功劳归功于 PeterG。谢啦。 https://whosebug.com/users/10070561/peterg