如何在 LINQ where 子句中传递 func 表达式?

Posted

技术标签:

【中文标题】如何在 LINQ where 子句中传递 func 表达式?【英文标题】:How to pass func expression in LINQ where clause? 【发布时间】:2021-11-10 10:27:18 【问题描述】:

这是我在 where 子句中传递的自定义过滤器(Func)

Func<Project,bool> filter = f =>

    bool filteredContent = true;
    if (!CreatorId.Equals(0))
        filteredContent = f.CreatedBy.Equals(CreatorId);

    if (filteredContent && !VerticalMarketId.Equals(0))
        filteredContent = f.VerticalMarketsId.Equals(VerticalMarketId);

    if (filteredContent && !ProductCategoryId.Equals(0))
        filteredContent = f.ProductCategoriesId.Equals(ProductCategoryId);

    return filteredContent;

;

这是我的代码,我根据过滤器表达式中创建的条件获取所有项目

 getProjects = await _context.Projects.Where(x => x.IsDeleted == false && filter.Invoke(x))// Here I'm getting the exception
                .Include(PC => PC.ProjectComments.Where(x => x.IsDeleted == false))
                .Include(SP => SP.SharedProjects)
                .AsNoTracking().ToListAsync();

异常:无法使用 LINQ 表达式 (DbSet......) 翻译。要么以可以翻译的形式重写查询, 或通过插入调用显式切换到客户端评估 “AsEnumerable”、“AsAsyncEnumerable”、“ToList”或“ToListAsync”。

谁能告诉我如何使用表达式过滤数据?

注意:我可以在应用过滤器之前执行 ToListAsync(),但它会从数据库中获取所有记录,然后在客户端过滤。但我想过滤服务器端的数据。

【问题讨论】:

您需要Expression&lt;Func&lt;Project, bool&gt;&gt;,EF 才能翻译它 您能详细说明一下吗? @HansKesting 【参考方案1】:

如果您使用 Linq To Objects 应该可以工作,但您正在使用 Linq To SQL,在这种情况下,您必须考虑如何将此函数转换为有效的 SQL 语句。问问自己:我怎样才能在 SQL 语句中传递这个函数调用?根据您对表达式主体所做的操作,您无法将其转换为 SQL,有时您必须更简单。

候选解决方案

    在您的项目中添加PredicateBuilder 类。它将为您提供轻松的逻辑运算符来处理表达式。

    http://www.albahari.com/nutshell/predicatebuilder.aspx

    尝试定义一个表达式并将其作为参数传递给您的查询方法链的Where 方法。示例(阅读 cmets):

// define a expression with default condition
Expression<Func<Project, bool>> filter = f => !f.IsDeleted;

// check conditions to add new filtes with `And` logical operator
if (!CreatorId.Equals(0))
    filter = filter.And(f => f.CreatedBy.Equals(CreatorId));
else if (!VerticalMarketId.Equals(0))
    filter =  filter.And(f => f.VerticalMarketsId.Equals(VerticalMarketId));
else if (!ProductCategoryId.Equals(0))
    filter =  filter.And(f => f.ProductCategoriesId.Equals(ProductCategoryId));

// apply the filter on the query and execute it
getProjects = await _context.Projects.Where(filter)
    .Include(PC => PC.ProjectComments.Where(x => !x.IsDeleted))
    .Include(SP => SP.SharedProjects)
    .AsNoTracking()
    .ToListAsync();

注意:我没有测试此代码,它可能应该以某种方式修复!

关于 Linq To SQL 的重要提示:

逻辑运算符没问题,而且往往可以很好地翻译成 sql; Where(x =&gt; x.Children.Any(j =&gt; j.Children.Any())),每个 Any 调用都会在查询范围内生成一个子查询,请小心使用它,因为它可能会损害您的数据库性能。 如果您只需要检查项目是否存在,请使用queryable.Any(expression)。 如果您需要检查然后做某事,最好使用queryable.FirstOrDefault(expression),并在使用前检查结果是否为空。 通过.Take(int).Skip(int) 使用分页。 始终通过调用.ToList().ToArray() 或这些方法的异步版本来具体化您的查询。避免在顶层传递可查询(查询可以在您想要的范围之外执行)。

【讨论】:

它不工作。 .and 方法要求多一个参数,它需要两个参数左表达式右表达式。这是什么?【参考方案2】:

我通过创建一个简单的表达式来解决这个问题,如下所示:

private static Expression<Func<Project, bool>> ProjectFilterExpression(
    int creatorId, 
    int verticalMarketId, 
    int productCategoryId)

    Expression<Func<Project, bool>> projectFilterExpression = pfe => 
    !pfe.IsDeleted
    //CreatorId Filter
    && (creatorId.Equals(0) || pfe.CreatedBy.Equals(creatorId))
    //Vertical Market Filter
    && (verticalMarketId.Equals(0) || pfe.VerticalMarketsId.Equals(verticalMarketId))
    // Product Category Filter
    && (productCategoryId.Equals(0) || pfe.ProductCategoriesId.Equals(productCategoryId)); 
    return projectFilterExpression;

然后我在我的过滤器方法中调用这个静态方法。

var filter = ProjectFilterExpression(CreatorId, VerticalMarketId, ProductCategoryId);

最后我在我的 LINQ where 子句中应用了这个过滤器

getProjects = await _context.Projects.Where(filter).AsNoTracking().ToListAsync();

一切正常。

【讨论】:

与其尝试构建单个Expression&lt;&gt;,因为您的标准都是&amp;&amp;s,我将有条件地调用.Where 来添加每个过滤器。例如; query = _context.Projects.Where(x =&gt; !x.IsDeleted); if (cond) query = query.Where(f =&gt; [filter]); @JeremyLakeman 您的建议肯定会奏效。但在您的建议中,它将根据第一个条件从数据库中获取数据。然后它将对包含数据的 C# 对象应用其他条件。我的查询是.. 我想发送一个表达式,基于哪些数据将仅在 DB 中过滤。所以我不必从 db 中获取所有数据,然后在 C# 中对其进行过滤。这需要更多时间。 没有。您应该将IQueryable 视为查询构建器。在您通过.ToList().Sum(...) 等枚举结果之前,什么都不会发生。

以上是关于如何在 LINQ where 子句中传递 func 表达式?的主要内容,如果未能解决你的问题,请参考以下文章

Linq - 如何在 where 子句中使用 2 列

如何在使用 Linq 的 Where 子句之后选择数组索引?

如何将重复的 where 子句表达式从 linq 拉到函数中?

过滤器设计中的 Linq 铸造

Linq to Entities 中的动态 where 子句 (OR)

如何在linq where子句中将int与字符串进行比较[重复]