1.创建需要的对象,如下代码:
public class QueryCondition
{public List<QueryObject> QueryObjects { get; set; }public List<OrderByObject> OrderBys { get; set; }public string LogicalOperator { get; set; }
}public class QueryObject
{public string PropertyName { get; set; }public string Operator { get; set; }public object Value { get; set; }
}public class OrderByObject
{public string PropertyName { get; set; }public bool Ascending { get; set; }
}
2.构建个QueryableExtension类,写个方法BuildMultiConditionQueryExpression
public static class QueryableExtensions{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;}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);}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); }}
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"
}