在LINQ中如何查询条件不固定,如何合并两个lambda表达式?其中一个方式是LINQ.Dynamic,关于LINQ.Dynamic的简单使用可以参考这篇文章,还有一种方法是利用Expression表达式树,有关表达式树的介绍,可以看这篇文章。
测试代码如下:
public class Phone
{public string Country { get; set; }public string City { get; set; }public string Name { get; set; }
}
public class Person
{public string Name { get; set; }public string Gender { get; set; }public int Age { get; set; }public List<Phone> Phones { get; set; }}
static void Main(string[] args)
{List<Person> PersonLists = new List<Person>(){new Person { Name = "张三",Age = 20,Gender = "男",Phones = new List<Phone> {new Phone { Country = "中国", City = "北京", Name = "小米" },new Phone { Country = "中国",City = "北京",Name = "华为"},new Phone { Country = "中国",City = "北京",Name = "联想"},new Phone { Country = "中国",City = "台北",Name = "魅族"},}},new Person { Name = "松下",Age = 30,Gender = "男",Phones = new List<Phone> {new Phone { Country = "日本",City = "东京",Name = "索尼"},new Phone { Country = "日本",City = "大阪",Name = "夏普"},new Phone { Country = "日本",City = "东京",Name = "松下"},}},new Person { Name = "克里斯",Age = 40,Gender = "男",Phones = new List<Phone> {new Phone { Country = "美国",City = "加州",Name = "苹果"},new Phone { Country = "美国",City = "华盛顿",Name = "三星"},new Phone { Country = "美国",City = "华盛顿",Name = "HTC"}}}};Expression<Func<Person, bool>> lambdaone = ex => ex.Name.Equals("张三");Expression<Func<Person, bool>> lambdatwo = ex => ex.Age == 30;ParameterExpression pa = Expression.Parameter(typeof(Person), "ex");Expression<Func<Person,bool>> newEx = Expression.Lambda<Func<Person,bool>>(Expression.Or(lambdaone.Body, lambdatwo.Body), pa);var Lists = PersonLists.Where(newEx.Compile());Console.WriteLine();Console.Read();
}
在上述代码中,我要构建一个满足两个条件的过滤查询:名字叫张三或者年龄是30。运行结果如下:
结果报错了,说明没有想象的那么简单。原因是即便我的命名是相同的,都是ex,但是两个表达式本质并不是相同的参数ex ,我只是引用了两个不同parameter的lambda表达式的body,然后将这两个body强行给了另一个parameter,具体信息可以这篇文藏
这时候我们需要重写ExpressionVisitor,代码如下:
public class MyExpressionVisitor : ExpressionVisitor{public ParameterExpression _Parameter { get; set; }public MyExpressionVisitor(ParameterExpression Parameter){_Parameter = Parameter;}protected override Expression VisitParameter(ParameterExpression p){return _Parameter;}public override Expression Visit(Expression node){return base.Visit(node);//Visit会根据VisitParameter()方法返回的Expression修改这里的node变量}}
修改调试代码如下:
Expression<Func<Person, bool>> lambdaone = ex => ex.Name.Equals("张三");
Expression<Func<Person, bool>> lambdatwo = ex => ex.Age == 30;
ParameterExpression pa = Expression.Parameter(typeof(Person), "ex");
MyExpressionVisitor visitor = new MyExpressionVisitor(pa);//统一参数类型
Expression bodyone = visitor.Visit(lambdaone.Body);
Expression bodytwo = visitor.Visit(lambdatwo.Body);
Expression<Func<Person, bool>> newEx = Expression.Lambda<Func<Person, bool>>(Expression.Or(bodyone, bodytwo), pa);
var Lists = PersonLists.Where(newEx.Compile());
foreach (var List in Lists)
{Console.WriteLine(List.Name);
}
Console.Read();
从运行结果来看,是没问题的
第二种方式是利用Expression.Invoke() ,代码如下:
Expression<Func<Person, bool>> lambdaone = ex => ex.Name.Equals("张三");
Expression<Func<Person, bool>> lambdatwo = ex => ex.Age == 30;
// 创建参数表达式
InvocationExpression invocation = Expression.Invoke(lambdaone, lambdatwo.Parameters.Cast<Expression>());
// 创建or运算
BinaryExpression binary = Expression.Or(lambdatwo.Body, invocation);
// 生成lambda表达式
var exp = Expression.Lambda<Func<Person, bool>>(binary, lambdatwo.Parameters);
var Lists = PersonLists.Where(exp.Compile());
foreach (var List in Lists)
{Console.WriteLine(List.Name);
}
Console.Read();
运行结果相同: