Bootstrap

.NET 使用Expression构造多条件动态查询表达式树方法

1.创建需要的对象,如下代码:

/// <summary>
/// 查询条件
/// </summary>
public class QueryCondition
{
    /// <summary>
    /// 查询对象集合
    /// </summary>
    public List<QueryObject> QueryObjects { get; set; }
    /// <summary>
    /// 排序对象集合
    /// </summary>
    public List<OrderByObject> OrderBys { get; set; }
    /// <summary>
    /// 逻辑操作
    /// </summary>
    public string LogicalOperator { get; set; }
}

public class QueryObject
{
    /// <summary>
    /// 属性名称
    /// </summary>
    public string PropertyName { get; set; }
    /// <summary>
    /// 操作符
    /// </summary>
    public string Operator { get; set; }
    /// <summary>
    /// 值
    /// </summary>
    public object Value { get; set; }
}

public class OrderByObject
{
    /// <summary>
    /// 属性名称
    /// </summary>
    public string PropertyName { get; set; }
    /// <summary>
    /// 是否升序(true-升序;false-降序;)
    /// </summary>
    public bool Ascending { get; set; }
}

2.构建个QueryableExtension类,写个方法BuildMultiConditionQueryExpression

 public static class QueryableExtensions
 {
     /// <summary>
     /// 动态查询
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="source"></param>
     /// <param name="conditions"></param>
     /// <param name="logicalOperator"></param>
     /// <param name="orderBys"></param>
     /// <returns></returns>
     public static IQueryable<T> DynamicQuery<T>(this IQueryable<T> source, List<QueryObject> conditions, string logicalOperator, List<OrderByObject> orderBys)
     {
         // 应用查询条件
         var predicate = BuildQueryExpression<T>(conditions, logicalOperator);
         var query = source.Where(predicate);

         // 应用排序条件
         if (orderBys != null && orderBys.Any())
         {
             query = ApplyOrderBy(query, orderBys);
         }

         return query;
     }

     /// <summary>
     /// 构建查询表达式
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="conditions"></param>
     /// <param name="logicalOperator"></param>
     /// <returns></returns>
     /// <exception cref="ArgumentException"></exception>
     public static Expression<Func<T, bool>> BuildQueryExpression<T>(List<QueryObject> conditions, string logicalOperator)
     {
         var parameter = Expression.Parameter(typeof(T), "x");

         // 创建一个列表来存储所有条件表达式
         var expressions = new List<Expression>();

         foreach (var condition in conditions)
         {
             var property = typeof(T).GetProperty(condition.PropertyName);
             if (property == null)
             {
                 throw new ArgumentException($"Property '{condition.PropertyName}' not found on type '{typeof(T).Name}'.");
             }

             var propertyAccess = Expression.MakeMemberAccess(parameter, property);

             Expression comparisonExpression;
             switch (condition.Operator.ToLower())
             {
                 case "=":
                 case "==":
                     var constantValueEqual = Expression.Constant(condition.Value, property.PropertyType);
                     comparisonExpression = Expression.Equal(propertyAccess, constantValueEqual);
                     break;
                 case "!=":
                     var constantValueNotEqual = Expression.Constant(condition.Value, property.PropertyType);
                     comparisonExpression = Expression.NotEqual(propertyAccess, constantValueNotEqual);
                     break;
                 case ">":
                     var constantValueGreaterThan = Expression.Constant(condition.Value, property.PropertyType);
                     comparisonExpression = Expression.GreaterThan(propertyAccess, constantValueGreaterThan);
                     break;
                 case ">=":
                     var constantValueGreaterThanOrEqual = Expression.Constant(condition.Value, property.PropertyType);
                     comparisonExpression = Expression.GreaterThanOrEqual(propertyAccess, constantValueGreaterThanOrEqual);
                     break;
                 case "<":
                     var constantValueLessThan = Expression.Constant(condition.Value, property.PropertyType);
                     comparisonExpression = Expression.LessThan(propertyAccess, constantValueLessThan);
                     break;
                 case "<=":
                     var constantValueLessThanOrEqual = Expression.Constant(condition.Value, property.PropertyType);
                     comparisonExpression = Expression.LessThanOrEqual(propertyAccess, constantValueLessThanOrEqual);
                     break;
                 case "contains":
                     var methodInfo_contains = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                     var constantValueContains = Expression.Constant(condition.Value.ToString(), typeof(string));
                     comparisonExpression = Expression.Call(propertyAccess, methodInfo_contains, constantValueContains);
                     break;
                 case "startswith":
                     var methodInfo_startswith = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
                     var constantValueStartsWith = Expression.Constant(condition.Value.ToString(), typeof(string));
                     comparisonExpression = Expression.Call(propertyAccess, methodInfo_startswith, constantValueStartsWith);
                     break;
                 case "endswith":
                     var methodInfo_endswith = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
                     var constantValueEndsWith = Expression.Constant(condition.Value.ToString(), typeof(string));
                     comparisonExpression = Expression.Call(propertyAccess, methodInfo_endswith, constantValueEndsWith);
                     break;
                 default:
                     throw new ArgumentException($"Unsupported comparison operator: {condition.Operator}");
             }

             expressions.Add(comparisonExpression);
         }

         // 根据逻辑操作符组合所有条件
         Expression combinedExpression;
         if (logicalOperator.ToLower() == "and")
         {
             combinedExpression = expressions.Aggregate((acc, expr) => Expression.AndAlso(acc, expr));
         }
         else if (logicalOperator.ToLower() == "or")
         {
             combinedExpression = expressions.Aggregate((acc, expr) => Expression.OrElse(acc, expr));
         }
         else
         {
             throw new ArgumentException($"Unsupported logical operator: {logicalOperator}");
         }

         // 返回完整的表达式
         return Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
     }

     /// <summary>
     /// 应用排序
     /// </summary>
     /// <typeparam name="T"></typeparam>
     /// <param name="source"></param>
     /// <param name="orderBys"></param>
     /// <returns></returns>
     /// <exception cref="ArgumentException"></exception>
     public static IOrderedQueryable<T> ApplyOrderBy<T>(IQueryable<T> source, List<OrderByObject> orderBys)
     {
         IOrderedQueryable<T> orderedSource = null;

         foreach (var orderBy in orderBys)
         {
             var parameter = Expression.Parameter(typeof(T), "x");
             var property = typeof(T).GetProperty(orderBy.PropertyName);
             if (property == null)
             {
                 throw new ArgumentException($"Property '{orderBy.PropertyName}' not found on type '{typeof(T).Name}'.");
             }

             var propertyAccess = Expression.MakeMemberAccess(parameter, property);
             var keySelector = Expression.Lambda(propertyAccess, parameter);

             var methodName = orderBy.Ascending ? "OrderBy" : "OrderByDescending";
             if (orderedSource != null)
             {
                 methodName = orderBy.Ascending ? "ThenBy" : "ThenByDescending";
             }

             var resultExpression = Expression.Call(
                 typeof(Queryable),
                 methodName,
                 new[] { typeof(T), property.PropertyType },
                 orderedSource != null ? orderedSource.Expression : source.Expression,
                 Expression.Quote(keySelector));

             orderedSource = (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExpression);
         }

         return orderedSource ?? source.AsQueryable().OrderBy(x => true); // Fallback to a no-op order by
     }
 }

3.测试接口如下:

[HttpPost]
public IActionResult DynamicQueryAndSort(QueryCondition queryCondition)
{
    try
    {
        var productList = new List<Product>()
        {
            new Product{Id = 1,Name = "p1",Price = 11},
            new Product{Id = 2,Name = "p2",Price = 12},
            new Product{Id = 3,Name = "p3",Price = 13},
            new Product{Id = 4,Name = "p4",Price = 14},
            new Product{Id = 5,Name = "p5",Price = 15},
        };

        var result = productList.AsQueryable().DynamicQuery(queryCondition.QueryObjects, queryCondition.LogicalOperator, queryCondition.OrderBys).ToList();

        return Ok(result);
    }
    catch (Exception ex)
    {

        throw;
    }
}

4.接口入参如下:

{
  "queryObjects": [
    {
      "propertyName": "Name",
      "operator": "=",
      "value": "p4"
    },
{
      "propertyName": "Name",
      "operator": "=",
      "value": "p5"
    }
  ],
  "orderBys": [
    {
      "propertyName": "Name",
      "ascending": false
    }
  ],
  "logicalOperator": "or"
}
;