.NET 6中PredicateBuilder动态属性名调用Object.GetProperty失效问题
WinUI 3 .NET 6 动态属性名构建Lambda条件的问题解决
场景与问题
我正在开发WinUI 3 .NET 6应用,需要基于动态属性名构建Lambda过滤条件,使用自定义的PredicateBuilder实现。相关核心代码如下:
private Expression<Func<MyModel, bool>> PrepareFilterExpression(MyModel model) { try { if (model is null) return null; var predicate = PredicateBuilder.False<MyModel>(); model.Conditions.ForEach(c => { var operatorType = c.ExpressionOperator ?? ExpressionOperatorType.Or; var comparerType = c.Comparer ?? ExpressionComparerType.EqualTo; string propertyName = c.Key; GetExpression(ref predicate, propertyName, operatorType, comparerType, c.Value); }); return predicate; } catch (Exception ex) { // 异常处理 } return null; } private void GetExpression(ref Expression<Func<MyModel, bool>> predicate, string propertyName, ExpressionOperatorType operatorType, ExpressionComparerType comparerType, string propertyValue) { switch (operatorType) { case ExpressionOperatorType.Or: case ExpressionOperatorType.None: switch (comparerType) { // TODO: 替换静态属性名为变量propertyName case ExpressionComparerType.Contains: predicate = predicate.Or(w => Convert.ToString(w.GetType().GetProperty("Title").GetValue(w)).Contains(propertyValue, StringComparison.OrdinalIgnoreCase)); break; case ExpressionComparerType.EqualTo: predicate = predicate.Or(w => Convert.ToString(w.GetType().GetProperty("Title").GetValue(w)).Equals(propertyValue, StringComparison.OrdinalIgnoreCase)); break; } break; default: break; } }
遇到的问题:当硬编码静态属性名(如"Title")时,predicate.Or()/And()可以正常工作;但改用变量propertyName传递属性名时,Object.GetProperty()无法获取对应的PropertyInfo,导致功能失效。
问题原因
- 大小写不匹配:
Type.GetProperty()默认区分大小写,如果传入的propertyName与实际属性名大小写不一致,会返回null。硬编码的静态字符串大小写正确,所以能正常获取属性。 - 绑定标志缺失:
GetProperty()默认仅查找public实例属性,如果目标属性是非public(如internal),未指定BindingFlags会导致无法找到。 - 类型获取错误:使用
w.GetType()会获取实例的实际类型(如果MyModel有子类),而非MyModel本身的类型,若子类没有该属性则会返回null。 - 硬编码未替换:代码中
GetExpression方法仍保留硬编码的"Title",未实际替换为propertyName变量(TODO注释已标注)。
解决办法
方案1:直接使用PredicateBuilder已封装的方法
你的PredicateBuilder已经实现了CompareOr和CompareAnd方法,这些方法已处理动态属性名的逻辑,直接调用即可避免手动反射的错误:
private void GetExpression(ref Expression<Func<MyModel, bool>> predicate, string propertyName, ExpressionOperatorType operatorType, ExpressionComparerType comparerType, string propertyValue) { switch (operatorType) { case ExpressionOperatorType.Or: case ExpressionOperatorType.None: predicate = predicate.CompareOr(propertyName, propertyValue, comparerType); break; case ExpressionOperatorType.And: predicate = predicate.CompareAnd(propertyName, propertyValue, comparerType); break; default: break; } }
方案2:修正手动反射的逻辑
如果需要手动构建表达式,需解决反射的问题,并优先使用Expression树构建(而非在Lambda中嵌入反射,避免LINQ解析失败):
private void GetExpression(ref Expression<Func<MyModel, bool>> predicate, string propertyName, ExpressionOperatorType operatorType, ExpressionComparerType comparerType, string propertyValue) { // 1. 获取MyModel的属性,指定绑定标志确保大小写不敏感且能找到非public属性(按需调整) var propertyInfo = typeof(MyModel).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); if (propertyInfo == null) return; // 2. 构建Expression树 var parameter = Expression.Parameter(typeof(MyModel), "w"); var propertyAccess = Expression.Property(parameter, propertyInfo); // 转换为string var toStringExpr = Expression.Call(typeof(Convert), "ToString", Type.EmptyTypes, propertyAccess); Expression conditionExpr; switch (comparerType) { case ExpressionComparerType.Contains: var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string), typeof(StringComparison) }); conditionExpr = Expression.Call(toStringExpr, containsMethod, Expression.Constant(propertyValue), Expression.Constant(StringComparison.OrdinalIgnoreCase)); break; case ExpressionComparerType.EqualTo: var equalsMethod = typeof(string).GetMethod("Equals", new[] { typeof(string), typeof(StringComparison) }); conditionExpr = Expression.Call(toStringExpr, equalsMethod, Expression.Constant(propertyValue), Expression.Constant(StringComparison.OrdinalIgnoreCase)); break; default: conditionExpr = Expression.Constant(true); break; } // 3. 合并到predicate var lambda = Expression.Lambda<Func<MyModel, bool>>(conditionExpr, parameter); predicate = operatorType switch { ExpressionOperatorType.And => predicate.And(lambda), ExpressionOperatorType.Or or ExpressionOperatorType.None => predicate.Or(lambda), _ => predicate }; }
关键修正点
- 使用
typeof(MyModel)代替w.GetType(),确保查找基类的属性。 - 添加
BindingFlags.IgnoreCase解决大小写问题,按需添加BindingFlags.NonPublic支持非public属性。 - 用
Expression树构建条件,避免在Lambda中嵌入反射,确保表达式能被LINQ提供者(如EF Core)解析。
内容的提问来源于stack exchange,提问作者Sulay Joshi
相关产品推荐
相关产品推荐

