在这个例子中,我有一个People列表,其中包含一些随机数据,这些数据正在被许多选项过滤。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
var people = GetPeople();
ConsolePeople(GetPeopleFiltered(GetFilters(new FilterRequest {Male = true}), people));
ConsolePeople(GetPeopleFiltered(GetFilters(new FilterRequest {Female= true}), people));
ConsolePeople(GetPeopleFiltered(GetFilters(new FilterRequest {Male = true, TwentyToThirty = true}),people));
ConsolePeople(GetPeopleFiltered(GetFilters(new FilterRequest {Male = true, Female=true, TwentyToThirty = true}),people));
}
public static void ConsolePeople(List<Person> people)
{
if(people.Count == 0)
Console.WriteLine("No people found");
foreach(var person in people)
{
Console.WriteLine(string.Format("FirstName: {0}, LastName: {1}, Age: {2}, Gender: {3}", person.FirstName, person.LastName, person.Age, person.Gender.ToString()));
}
Console.WriteLine(string.Empty);
}
public static List<Person> GetPeople()
{
var people = new List<Person>();
people.Add(new Person { FirstName = "Philip", LastName = "Smith", Age = 29, Gender = GenderEnum.Male});
people.Add(new Person { FirstName = "Joe", LastName = "Blogs", Age = 40, Gender = GenderEnum.Male});
people.Add(new Person { FirstName = "Mary", LastName = "Ann", Age = 10, Gender = GenderEnum.Female});
people.Add(new Person { FirstName = "Lisa", LastName = "Dunn", Age = 60, Gender = GenderEnum.Male});
people.Add(new Person { FirstName = "Terry", LastName = "Banks", Age = 89, Gender = GenderEnum.Male});
people.Add(new Person { FirstName = "John", LastName = "Doe", Age = 32, Gender = GenderEnum.Male});
people.Add(new Person { FirstName = "Sally", LastName = "Shields", Age = 19, Gender = GenderEnum.Female});
return people;
}
public static List<Expression<Func<Person, bool>>> GetFilters(FilterRequest request)
{
var filters = new List<Expression<Func<Person, bool>>>();
if(request.Male)
filters.Add(x=>x.Gender == GenderEnum.Male);
if(request.Female)
filters.Add(x=>x.Gender == GenderEnum.Female);
if(request.TentoTwenty)
filters.Add(x=>x.Age >= 10 && x.Age < 20);
if(request.TwentyToThirty)
filters.Add(x=>x.Age >= 20 && x.Age < 30);
if(request.ThirtyToFourty)
filters.Add(x=>x.Age >= 30 && x.Age < 40);
if(request.FourtyPlus)
filters.Add(x=>x.Age >= 40);
return filters;
}
public static List<Person> GetPeopleFiltered(List<Expression<Func<Person,bool>>> filters, List<Person> people)
{
var query = people.AsQueryable();
foreach(var filter in filters)
{
query = query.Where(filter);
}
return query.ToList();
}
}
public class FilterRequest
{
public bool Male {get;set;}
public bool Female {get;set;}
public bool TentoTwenty {get;set;}
public bool TwentyToThirty {get;set;}
public bool ThirtyToFourty {get;set;}
public bool FourtyPlus {get;set;}
}
public class Person
{
public string FirstName {get;set;}
public string LastName {get;set;}
public int Age {get;set;}
public GenderEnum Gender {get;set;}
}
public enum GenderEnum
{
Male,
Female
}
您可以在DotNetFiddle
看到这一点我希望我的List<Expression<Func<Person, bool>>>
成为||列表在某些情况下的条款。因此,在这个例子中,如果您同时选择了男性和女性以及年龄范围,那么我希望
(x.Gender == GenderEnum.Male || x.Gender == GenderEnum.Female)
&& ((x.Age > 10 && x.Age < 20) || (x.Age >= 20 && x.Age < 30))
我如何实现这一目标?我知道这个例子可能会有不同的改动,但它只是一个例子。
注意:真正的代码将对数百万行信息进行处理,因此应该进行相当优化。
答案 0 :(得分:2)
以下是一个PredicateBuilder
的实现,它能够Or
两个表达式在一起:
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
这允许你写:
var predicate = listOfPredicateExpressions.Aggregate(PredicateBuilder.Or);
答案 1 :(得分:-1)
您要做的是对同一类别的过滤器进行分组。例如,应该使用两个性别过滤器,而应该使用性别过滤器和年龄过滤器。因此,您需要替换返回过滤器的方法,以返回可枚举的过滤器的可枚举数。每个枚举代表您可以拥有的一类过滤器。
在上面给出的示例中,您将得到一个如下所示的对象:
{
{
x => x.Gender == GenderEnum.Male,
x => x.Gender == GenderEnum.Female
},
{
x => x.Age >= 10 && x.Age < 20,
x => x.Age >= 20 && x.Age < 30
}
}
您的查询将转换为以下方法:
public static List<Person> GetPeopleFiltered(IEnumerable<IEnumerable<Func<Person,bool>>> filterCategories, List<Person> people)
{
var query = people;
foreach(var filterCat in filterCategories)
{
query = query.Where(x => filterCat.Any(f => f(x)));
}
return query.ToList();
}
或者,也可以摆脱外部的foreach循环:
public static List<Person> GetPeopleFiltered(IEnumerable<IEnumerable<Func<Person,bool>>> filterCategories, List<Person> people)
{
return people.Where(x => filterCategories.All(cat => cat.Any(f => f(x)))).ToList();
}
Any
方法遍历集合的所有元素,如果遇到产生true的元素,则返回true。 All
执行相同操作,但如果它枚举一个产生错误的元素,则返回false。