C#的谓词列表传递到Linq Where子句

9

我有一个很长的Linq Where 子句,我想用谓词列表填充它。

List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();

filters.Add(p => p.Title != null && p.Title.ToLower().Contains(searchString));
filters.Add(p => p.Notes != null && p.Notes.ToLower().Contains(searchString));
filters.Add(GlobalSearchUser((List < User > users = new List<User>() { p.user1, p.user2, p.user3, p.user4 }), searchString));

notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
      .Where(filters.ToArray()).Take(10).ToList();

然而,我遇到了这个错误:

无法将 'System.Linq.Expressions.Expression<System.Func<project.Contracts.DTOs.Note,bool>>[]' 转换为 'System.Func<project.Contracts.DTOs.Note,bool>'

这是在.where子句上的错误。去掉.where之后编译就可以通过。


我认为这是最佳解决方案:https://dev59.com/EmQn5IYBdhLWcg3wn4RS#46436773 - Imran Faruqi
4个回答

11
我认为Hogan所提供的优秀答案可以通过使用AnyAll Linq方法来简化并缩短一点。
获取满足所有条件的项目:
var resultAll = listOfItems.Where(p => filters.All(f => f(p)));

获取满足任何条件的项目:

var resultAny = listOfItems.Where(p => filters.Any(f => f(p)));

这种技术一次只能评估一个条件,不能同时评估所有条件。如果您需要同时评估所有筛选器,请查看PredicateBuilder。 - Fer R

8

你的代码中至少有两个错误:

List<Expression<Func<Note, bool>>> filters = new List<Expression<Func<Note, bool>>>();

将其更改为

List<Func<Note, bool>> filters = new List<Func<Note, bool>>();

您在这里不需要使用表达式树,因为您正在使用的是“IEnumerable<>”,而非“IQueryable<>”。
notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
  .Where(filters.ToArray()).Take(10).ToList();

.Where() 一次只接受一个谓词。你可以:

notes = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
  .Where(x => filters.All(x)).Take(10).ToList();

或其他各种解决方案,例如:
var notesEnu = dataAccess.GetList<Note>(pn => pn.ProjectVersionID == projectVersionID, filterExtensions.ToArray())
              .AsEnumerable();

foreach (var filter in filters)
{
    notesEmu = notesEmu.Where(filter);
}

notes = notesEnu.Take(10).ToList();

因为所有的.Where()条件都隐含在&&中。

4

您需要遍历您的筛选器并对每个进行测试。

您可以使用类似于linq的方法来执行此操作,如果任何一个筛选器为真,则返回true

.Where(p => { foreach(f in filters) if (f(p) == true) return(true); return(false)}) 

或者像这样,如果您的所有过滤器都为真,则返回true

.Where(p => { foreach(f in filters) if (f(p) == false) return(false); return(true)}) 

@moarboilerplate - 我不知道Aggregate()怎么会有帮助。 - Hogan
.Aggregate(GetList()..., (list, func) => list.Where(func)); 逐步过滤列表 - moarboilerplate
@moarboilerplate - 这将涉及重写整个数据访问框架 -- 似乎为了使用.Aggregate()而做出了很多工作。我确实同意.Aggregate()很酷,但我认为我不会走得那么远。 - Hogan
很好的观点,因为它需要通过查询提供程序而不仅仅是对象的linq。我应该提到,您可以通过将条件和布尔值替换为return f(p);来将您的答案简化为一个示例。 - moarboilerplate
@moarboilerplate - 不行,那个替换不起作用,if语句在foreach循环内部。 - Hogan
显示剩余5条评论

0

你不能直接将一个谓词数组传递给 where 方法。你需要迭代这个数组并且为其中每个表达式调用 Where() 方法,或者找到一种方法将它们全部合并成一个表达式并使用它。如果你选择第二条路线,你会想要使用 LinqKit。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接