C#中表达式树实现动态拼接lamda表达式查询条件
在C#中,表达式树(Expression Trees)可以用于动态构建和拼接Lambda表达式,特别是在需要动态生成查询条件时非常有用。通过表达式树,你可以在运行时构建复杂的查询条件,而不需要在编译时硬编码这些条件。
下面是一个简单的示例,展示如何使用表达式树动态拼接Lambda表达式查询条件。
示例场景
假设我们有一个Person
类,并且我们想要根据不同的条件动态查询Person
对象。
csharp
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
动态拼接Lambda表达式
我们可以使用Expression
类来动态构建查询条件。以下是一个示例,展示如何根据不同的条件动态拼接Lambda表达式。
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
var people = new List<Person>
{
new Person { Name = "Alice", Age = 25, City = "New York" },
new Person { Name = "Bob", Age = 30, City = "Los Angeles" },
new Person { Name = "Charlie", Age = 35, City = "New York" },
new Person { Name = "David", Age = 40, City = "Chicago" }
};
// 动态构建查询条件
Expression<Func<Person, bool>> condition = BuildCondition("New York", 30);
// 应用条件并过滤数据
var filteredPeople = people.AsQueryable().Where(condition).ToList();
foreach (var person in filteredPeople)
{
Console.WriteLine($"{person.Name}, {person.Age}, {person.City}");
}
}
public static Expression<Func<Person, bool>> BuildCondition(string city, int minAge)
{
// 创建参数表达式
var parameter = Expression.Parameter(typeof(Person), "p");
// 创建属性表达式
var cityProperty = Expression.Property(parameter, "City");
var ageProperty = Expression.Property(parameter, "Age");
// 创建常量表达式
var cityValue = Expression.Constant(city);
var minAgeValue = Expression.Constant(minAge);
// 创建比较表达式
var cityCondition = Expression.Equal(cityProperty, cityValue);
var ageCondition = Expression.GreaterThanOrEqual(ageProperty, minAgeValue);
// 组合条件
var combinedCondition = Expression.AndAlso(cityCondition, ageCondition);
// 创建Lambda表达式
return Expression.Lambda<Func<Person, bool>>(combinedCondition, parameter);
}
}
代码解释
-
BuildCondition
方法:该方法动态构建了一个Expression<Func<Person, bool>>
,表示一个Lambda表达式,用于过滤Person
对象。-
Expression.Parameter
:创建一个参数表达式,表示Lambda表达式的输入参数。 -
Expression.Property
:创建一个属性访问表达式,表示访问Person
对象的属性。 -
Expression.Constant
:创建一个常量表达式,表示常数值。 -
Expression.Equal
和Expression.GreaterThanOrEqual
:创建比较表达式,分别表示等于和大于等于的比较。 -
Expression.AndAlso
:将两个条件表达式组合成一个逻辑与(AND)表达式。 -
Expression.Lambda
:将组合后的表达式包装成一个Lambda表达式。
-
-
应用条件:在
Main
方法中,我们调用BuildCondition
方法生成查询条件,并将其应用于people
列表,过滤出符合条件的Person
对象。
输出
运行上述代码后,输出将是符合条件的Person
对象:
复制
Alice, 25, New York Charlie, 35, New York
如果你的查询条件个数是不确定的,可以通过动态组合多个条件表达式来实现。你可以使用Expression.AndAlso
或Expression.OrElse
来将多个条件表达式动态拼接成一个完整的表达式树。
以下是一个示例,展示如何根据不确定的条件个数动态拼接Lambda表达式查询条件。
示例场景2
假设我们有一个Person
类,并且我们想要根据用户提供的多个条件动态查询Person
对象。每个条件可能是一个属性与值的比较(例如City == "New York"
或Age >= 30
),我们需要将这些条件动态组合起来。
实现代码2
csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string City { get; set; }
}
public class Program
{
public static void Main()
{
var people = new List<Person>
{
new Person { Name = "Alice", Age = 25, City = "New York" },
new Person { Name = "Bob", Age = 30, City = "Los Angeles" },
new Person { Name = "Charlie", Age = 35, City = "New York" },
new Person { Name = "David", Age = 40, City = "Chicago" }
};
// 动态构建查询条件
var conditions = new List<Expression<Func<Person, bool>>>
{
p => p.City == "New York", // 条件1:City == "New York"
p => p.Age >= 30 // 条件2:Age >= 30
};
// 动态拼接条件
var combinedCondition = CombineConditions(conditions);
// 应用条件并过滤数据
var filteredPeople = people.AsQueryable().Where(combinedCondition).ToList();
foreach (var person in filteredPeople)
{
Console.WriteLine($"{person.Name}, {person.Age}, {person.City}");
}
}
/// <summary>
/// 动态拼接多个条件表达式
/// </summary>
/// <param name="conditions">条件列表</param>
/// <returns>拼接后的条件表达式</returns>
public static Expression<Func<Person, bool>> CombineConditions(List<Expression<Func<Person, bool>>> conditions)
{
if (conditions == null || !conditions.Any())
{
return p => true; // 如果没有条件,返回一个恒真条件
}
// 获取第一个条件
var combinedCondition = conditions[0];
// 遍历剩余条件,逐个拼接
for (int i = 1; i < conditions.Count; i++)
{
var nextCondition = conditions[i];
// 使用 Expression.AndAlso 拼接条件
var parameter = Expression.Parameter(typeof(Person), "p");
var body = Expression.AndAlso(
Expression.Invoke(combinedCondition, parameter),
Expression.Invoke(nextCondition, parameter)
);
combinedCondition = Expression.Lambda<Func<Person, bool>>(body, parameter);
}
return combinedCondition;
}
}
代码解释2
-
CombineConditions
方法:-
该方法接收一个
List<Expression<Func<Person, bool>>>
,表示多个条件表达式。 -
如果没有条件,返回一个恒真条件(
p => true
)。 -
使用
Expression.AndAlso
将多个条件动态拼接成一个完整的表达式树。
-
-
动态拼接条件:
-
使用
Expression.Invoke
调用每个条件表达式。 -
使用
Expression.AndAlso
将多个条件组合成一个逻辑与(AND)表达式。
-
-
应用条件:
-
在
Main
方法中,我们定义了两个条件(City == "New York"
和Age >= 30
),并将它们动态拼接成一个完整的查询条件。 -
使用
Where
方法将拼接后的条件应用于people
列表,过滤出符合条件的Person
对象。
-
输出2
运行上述代码后,输出将是符合条件的Person
对象:
复制
Charlie, 35, New York
支持更多条件
如果你需要支持更多的条件(例如OR
逻辑或更复杂的组合),可以扩展CombineConditions
方法。例如:
-
使用
Expression.OrElse
实现逻辑或(OR)组合。 -
支持动态选择逻辑运算符(AND 或 OR)。
扩展:支持动态逻辑运算符
以下是一个扩展版本,支持动态选择逻辑运算符(AND 或 OR):
csharp
public static Expression<Func<Person, bool>> CombineConditions(
List<Expression<Func<Person, bool>>> conditions,
Func<Expression, Expression, BinaryExpression> logicalOperator)
{
if (conditions == null || !conditions.Any())
{
return p => true; // 如果没有条件,返回一个恒真条件
}
// 获取第一个条件
var combinedCondition = conditions[0];
// 遍历剩余条件,逐个拼接
for (int i = 1; i < conditions.Count; i++)
{
var nextCondition = conditions[i];
// 使用指定的逻辑运算符拼接条件
var parameter = Expression.Parameter(typeof(Person), "p");
var body = logicalOperator(
Expression.Invoke(combinedCondition, parameter),
Expression.Invoke(nextCondition, parameter)
);
combinedCondition = Expression.Lambda<Func<Person, bool>>(body, parameter);
}
return combinedCondition;
}
调用时可以选择逻辑运算符:
csharp
// 使用 AND 逻辑
var combinedCondition = CombineConditions(conditions, Expression.AndAlso);
// 使用 OR 逻辑
var combinedCondition = CombineConditions(conditions, Expression.OrElse);
总结
通过动态拼接表达式树,你可以灵活地处理不确定数量的查询条件,并根据需要组合逻辑运算符(AND 或 OR)。这种方法非常适合动态查询场景,例如根据用户输入生成复杂的查询条件。