C# Linq、Lambda表达式树动态构建、合并条件扩展方法
前言
日常开发时,使用Linq和EF经常会在存在多条件查询,或者说动态条件查询时,便存在合并表达式树的情况。基于这种情况结合一些资料,写了个扩展类,代码如下:
代码实现
/// <summary>
/// Linq表达式扩展方法
/// </summary>
public static class PredicateExtensions
{
/// <summary>
/// 以And合并单个表达式
/// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeAnd<T>(this Expression<Func<T, bool>> leftExpress, Expression<Func<T, bool>> rightExpress)
{
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
//表达式树内容
Expression left = visitor.Visit(leftExpress.Body);
Expression right = visitor.Visit(rightExpress.Body);
//合并表达式
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
}
/// <summary>
/// 以And合并多个表达式
/// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeAnd<T>(this Expression<Func<T, bool>> express, params Expression<Func<T, bool>>[] arrayExpress)
{
if (!arrayExpress?.Any() ?? true) return express;
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
Expression<Func<T, bool>> result = null;
//合并表达式
foreach (var curExpression in arrayExpress)
{
//表达式树内容
Expression left = visitor.Visit(result.Body);
Expression right = visitor.Visit(curExpression.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
}
return result;
}
/// <summary>
/// 以Or合并表达式
/// 此处采用OrElse实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> leftExpress, Expression<Func<T, bool>> rightExpress)
{
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
//表达式树内容
Expression left = visitor.Visit(leftExpress.Body);
Expression right = visitor.Visit(rightExpress.Body);
//合并表达式
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), parameter);
}
/// <summary>
/// 以Or合并多个表达式
/// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
/// </summary>
public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> express, params Expression<Func<T, bool>>[] arrayExpress)
{
if (!arrayExpress?.Any() ?? true) return express;
//声明传递参数(也就是表达式树里面的参数别名s)
ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
//统一管理参数,保证参数一致,否则会报错
var visitor = new PredicateExpressionVisitor(parameter);
Expression<Func<T, bool>> result = null;
//合并表达式
foreach (var curExpression in arrayExpress)
{
//表达式树内容
Expression left = visitor.Visit(result.Body);
Expression right = visitor.Visit(curExpression.Body);
result = Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), parameter);
}
return result;
}
}
public class PredicateExpressionVisitor : ExpressionVisitor
{
public ParameterExpression _parameter { get; set; }
public PredicateExpressionVisitor(ParameterExpression parameter)
{
_parameter = parameter;
}
protected override Expression VisitParameter(ParameterExpression p)
{
return _parameter;
}
public override Expression Visit(Expression expression)
{
//Visit会根据VisitParameter()方法返回的Expression进行相关变量替换
return base.Visit(expression);
}
}
使用例子
class Program
{
static void Main(string[] args)
{
var models = new List<JsonData>() { new JsonData() { Id = "001", Name = "One" }, new JsonData() { Id = "002", Name = "Tow" } };
Console.WriteLine($"未处理集合:{string.Join(',', models.Select(o => o.Id))}");
//表达式1
Expression<Func<JsonData, bool>> expression1 = t => t.Id == "001";
//表达式2
Expression<Func<JsonData, bool>> expression2 = t => t.Name == "Tow";
//合并成 t => t.Id=="001" && t.Name=="One"
Expression<Func<JsonData, bool>> allEexpression = expression1.MergeAnd(expression2);
Console.WriteLine(allEexpression.Body.ToString());
Console.WriteLine($"已处理集合(And):{string.Join(',', models.Where(expression1.MergeAnd(expression2).Compile()).Select(o => o.Id))}");
//合并成 t => t.Id=="001" || t.Name=="One"
allEexpression = expression1.MergeOr(expression2);
Console.WriteLine(allEexpression.Body.ToString());
Console.WriteLine($"已处理集合(Or):{string.Join(',', models.Where(allEexpression.Compile()).Select(o => o.Id))}");
Console.ReadKey();
}
}
public class JsonData
{
public string Id { get; set; }
public string Name { get; set; }
}