如何在 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<Func<Project, bool>>
,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 => x.Children.Any(j => 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<>
,因为您的标准都是&&
s,我将有条件地调用.Where
来添加每个过滤器。例如; query = _context.Projects.Where(x => !x.IsDeleted); if (cond) query = query.Where(f => [filter]);
@JeremyLakeman 您的建议肯定会奏效。但在您的建议中,它将根据第一个条件从数据库中获取数据。然后它将对包含数据的 C# 对象应用其他条件。我的查询是.. 我想发送一个表达式,基于哪些数据将仅在 DB 中过滤。所以我不必从 db 中获取所有数据,然后在 C# 中对其进行过滤。这需要更多时间。
没有。您应该将IQueryable
视为查询构建器。在您通过.ToList()
.Sum(...)
等枚举结果之前,什么都不会发生。以上是关于如何在 LINQ where 子句中传递 func 表达式?的主要内容,如果未能解决你的问题,请参考以下文章
如何在使用 Linq 的 Where 子句之后选择数组索引?
如何将重复的 where 子句表达式从 linq 拉到函数中?