基于List<object>的Entity Framework动态Where子句实现问题
基于WhereClause列表生成Entity Framework动态Where子句的实现方案
我之前也做过类似的用户自定义查询需求,这里给你两种经过项目验证的可行实现思路,你可以根据场景选择:
方法一:手动构建表达式树(推荐,类型安全)
这种方式直接用.NET的表达式树API拼接查询条件,完全类型安全,还能避免SQL注入风险——EF会自动把表达式转换成参数化的SQL语句。
先写一个工具类封装表达式构建逻辑:
using System.Linq.Expressions; public static class DynamicQueryBuilder { // 根据实体类型和列名获取属性的表达式节点 private static MemberExpression GetPropertyExpression<T>(string columnName) { var parameter = Expression.Parameter(typeof(T), "entity"); return Expression.PropertyOrField(parameter, columnName); } // 根据操作符生成对应的比较表达式 private static Expression GetComparisonExpression(MemberExpression propertyExpr, string op, object value) { // 把用户输入的值转换成属性对应的类型,避免类型不匹配 var targetType = propertyExpr.Type; var convertedValue = Convert.ChangeType(value, targetType); var constantExpr = Expression.Constant(convertedValue, targetType); return op switch { "Equals" => Expression.Equal(propertyExpr, constantExpr), "NotEquals" => Expression.NotEqual(propertyExpr, constantExpr), "GreaterThan" => Expression.GreaterThan(propertyExpr, constantExpr), "GreaterThanOrEqual" => Expression.GreaterThanOrEqual(propertyExpr, constantExpr), "LessThan" => Expression.LessThan(propertyExpr, constantExpr), "LessThanOrEqual" => Expression.LessThanOrEqual(propertyExpr, constantExpr), "Contains" => Expression.Call( propertyExpr, typeof(string).GetMethod("Contains", new[] { typeof(string) })!, constantExpr ), "StartsWith" => Expression.Call( propertyExpr, typeof(string).GetMethod("StartsWith", new[] { typeof(string) })!, constantExpr ), // 可根据需求扩展更多操作符 _ => throw new NotSupportedException($"操作符「{op}」暂不支持") }; } // 把所有WhereClause合并成最终的查询表达式 public static Expression<Func<T, bool>> BuildWhereExpression<T>(List<WhereClause> clauses) { if (clauses?.Count == 0) { // 无过滤条件时返回始终为true的表达式,相当于不做筛选 return _ => true; } var parameter = Expression.Parameter(typeof(T), "entity"); Expression combinedExpr = null; foreach (var clause in clauses) { var propertyExpr = GetPropertyExpression<T>(clause.ColumnName); var currentCondition = GetComparisonExpression(propertyExpr, clause.Operator, clause.Value); if (combinedExpr == null) { // 第一个条件直接赋值 combinedExpr = currentCondition; } else { // 根据AndOr拼接逻辑与/或 combinedExpr = string.Equals(clause.AndOr, "And", StringComparison.OrdinalIgnoreCase) ? Expression.AndAlso(combinedExpr, currentCondition) : Expression.OrElse(combinedExpr, currentCondition); } } return Expression.Lambda<Func<T, bool>>(combinedExpr, parameter); } }
使用示例
假设你的EF实体是Product,DbSet为dbContext.Products,直接调用工具类生成表达式即可:
// 模拟用户通过窗体构建的查询条件 var userClauses = new List<WhereClause> { new WhereClause { ColumnName = "Price", Operator = "GreaterThan", Value = 100, AndOr = "And" }, new WhereClause { ColumnName = "Category", Operator = "Equals", Value = "Electronics" } }; // 生成过滤表达式 var filterExpr = DynamicQueryBuilder.BuildWhereExpression<Product>(userClauses); // 应用到EF查询上 var filteredProducts = dbContext.Products.Where(filterExpr).ToList();
方法二:使用Dynamic LINQ库(灵活快速)
如果更倾向于字符串拼接的方式,System.Linq.Dynamic.Core这个NuGet包可以帮你快速实现动态查询,注意要使用参数化避免注入风险。
首先安装NuGet包:Install-Package System.Linq.Dynamic.Core
然后写一个扩展方法处理条件拼接:
using System.Linq.Dynamic.Core; using System.Text; public static class DynamicQueryExtensions { public static IQueryable<T> ApplyDynamicWhere<T>(this IQueryable<T> query, List<WhereClause> clauses) { if (clauses?.Count == 0) { return query; } var conditionParts = new List<string>(); var parameters = new List<object>(); foreach (var clause in clauses) { // 用参数化方式避免SQL注入,参数名用@p0、@p1... var paramName = $"@{parameters.Count}"; var condition = clause.Operator switch { "Equals" => $"{clause.ColumnName} == {paramName}", "NotEquals" => $"{clause.ColumnName} != {paramName}", "GreaterThan" => $"{clause.ColumnName} > {paramName}", "GreaterThanOrEqual" => $"{clause.ColumnName} >= {paramName}", "LessThan" => $"{clause.ColumnName} < {paramName}", "LessThanOrEqual" => $"{clause.ColumnName} <= {paramName}", "Contains" => $"{clause.ColumnName}.Contains({paramName})", _ => throw new NotSupportedException($"操作符「{clause.Operator}」暂不支持") }; conditionParts.Add(condition); parameters.Add(clause.Value); } // 拼接所有条件,第一个条件不需要AndOr var fullCondition = new StringBuilder(conditionParts[0]); for (var i = 1; i < clauses.Count; i++) { fullCondition.Append($" {clauses[i].AndOr} {conditionParts[i]}"); } return query.Where(fullCondition.ToString(), parameters.ToArray()); } }
使用示例
直接用扩展方法给EF查询添加过滤条件:
var userClauses = new List<WhereClause> { new WhereClause { ColumnName = "Price", Operator = "LessThan", Value = 500, AndOr = "Or" }, new WhereClause { ColumnName = "InStock", Operator = "Equals", Value = true } }; var filteredProducts = dbContext.Products.ApplyDynamicWhere(userClauses).ToList();
注意事项
- 表达式树方法更安全,能在编译时检查列名是否存在,适合对类型安全要求高的场景;
- Dynamic LINQ更灵活,适合快速实现复杂动态查询,但要确保操作符和列名拼写正确;
- 若需处理空值(比如用户未输入Value),可在代码中添加判断,跳过空条件或处理
IsNull这类特殊操作; - 确保
WhereClause中的Operator字段与代码中switch分支对应,比如统一用"Equals"而非"=",避免匹配错误。
内容的提问来源于stack exchange,提问作者LCaraway
相关产品推荐
相关产品推荐

