Entity Framework仓储模式:同一属性多值时动态Where改OR实现
问题描述
基于Entity Framework的类型化仓储模式,我们实现了按属性名给任意表的任意列添加Where条件的功能,目前多条件默认采用AND逻辑。现在需要实现:同一属性对应多个值时,该属性的多条件改为OR逻辑;不同属性之间仍保持AND逻辑。现有代码通过循环调用AddWhereClause方法处理属性名与对应的值数组,请问如何修改该方法以实现需求?
现有代码
private static IQueryable<T> AddWhereClause(IQueryable<T> query, string propName, string propValue) { ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "e"); Expression makePropPath = MakePropPath(parameterExpression, propName); ConstantExpression value = null; string dataPropertyType = typeof(T).GetProperties() .FirstOrDefault(pi => pi.Name == propName)?.PropertyType.FullName; if (dataPropertyType.Contains("System.String")) { value = Expression.Constant(propValue, typeof(string)); } else if (dataPropertyType.Contains("System.Int")) { int? valueCast = Convert.ToInt32(propValue); value = Expression.Constant(valueCast, typeof(int?)); } else if (dataPropertyType.Contains("System.Double")) { double? valueCast = Convert.ToDouble(propValue); value = Expression.Constant(valueCast, typeof(double?)); } else if (dataPropertyType.Contains("System.Boolean")) { bool? valueCast = Convert.ToBoolean(propValue); value = Expression.Constant(valueCast, typeof(bool?)); } BinaryExpression body = Expression.Equal(makePropPath, value); Expression<Func<T, bool>> predicateLambda = Expression.Lambda<Func<T, bool>>(body, parameterExpression); return query.Where(predicateLambda); }
修改方案
核心思路是:让方法接受一个属性的多个值,将这些值对应的Equal表达式用Expression.OrElse组合成一个OR条件,再添加到查询中。不同属性的条件仍然通过多次调用方法保持AND逻辑(因为每次Where都是追加AND条件)。
具体修改步骤:
- 修改方法签名,将单值参数改为多值参数,支持传入同一属性的多个值
- 处理空值或空集合的边界情况,直接返回原查询
- 针对属性类型批量转换值,生成对应类型的常量表达式
- 用
Expression.OrElse将多个Equal表达式组合成OR逻辑链 - 生成Lambda表达式并追加到查询的Where条件中
修改后的完整代码
private static IQueryable<T> AddWhereClause(IQueryable<T> query, string propName, IEnumerable<string> propValues) { // 空值或空集合直接返回原查询 if (propValues == null || !propValues.Any()) return query; ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "e"); Expression propAccess = MakePropPath(parameterExpression, propName); var propertyInfo = typeof(T).GetProperties().FirstOrDefault(pi => pi.Name == propName); if (propertyInfo == null) return query; Type propType = propertyInfo.PropertyType; // 处理可空类型,获取底层类型 Type underlyingType = Nullable.GetUnderlyingType(propType) ?? propType; List<Expression> equalExpressions = new List<Expression>(); foreach (var propValue in propValues) { ConstantExpression value = null; try { // 根据属性类型转换值 object convertedValue = Convert.ChangeType(propValue, underlyingType); // 如果原属性是可空类型,包装成可空常量 if (Nullable.GetUnderlyingType(propType) != null) { value = Expression.Constant(convertedValue, propType); } else { value = Expression.Constant(convertedValue, underlyingType); } } catch { // 转换失败跳过该值,也可根据需求抛出异常 continue; } // 构建属性 == 值的表达式 equalExpressions.Add(Expression.Equal(propAccess, value)); } // 没有有效表达式则返回原查询 if (!equalExpressions.Any()) return query; // 组合所有Equal表达式为OR逻辑 Expression orBody = equalExpressions[0]; for (int i = 1; i < equalExpressions.Count; i++) { orBody = Expression.OrElse(orBody, equalExpressions[i]); } Expression<Func<T, bool>> predicateLambda = Expression.Lambda<Func<T, bool>>(orBody, parameterExpression); return query.Where(predicateLambda); }
关键说明
- OR逻辑选择:用
Expression.OrElse而非Expression.Or,前者对应C#的||短路逻辑,更适配EF的查询优化 - 可空类型兼容:通过
Nullable.GetUnderlyingType区分可空与非可空类型,保证常量表达式类型与属性匹配 - 错误容错:捕获值转换失败的异常,避免单个无效值导致整个查询崩溃,可根据业务调整为抛出异常
- 调用方式:同一属性的多个值只需一次传入方法;不同属性分别调用方法,自动保持AND逻辑
内容的提问来源于stack exchange,提问作者Stephen Himes
相关产品推荐
相关产品推荐

