You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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,导致功能失效。

问题原因

  1. 大小写不匹配:Type.GetProperty()默认区分大小写,如果传入的propertyName与实际属性名大小写不一致,会返回null。硬编码的静态字符串大小写正确,所以能正常获取属性。
  2. 绑定标志缺失:GetProperty()默认仅查找public实例属性,如果目标属性是非public(如internal),未指定BindingFlags会导致无法找到。
  3. 类型获取错误:使用w.GetType()会获取实例的实际类型(如果MyModel有子类),而非MyModel本身的类型,若子类没有该属性则会返回null。
  4. 硬编码未替换:代码中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:12:36