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

基于泛型的LINQ动态表达式过滤管理器返回结果为空问题排查

问题根源分析

你的过滤结果为空的核心问题在于调用Compare方法时传入的selector表达式是反射方法调用,而非直接的属性访问表达式,这导致表达式树无法正确解析属性值的类型匹配,最终比较逻辑失效。

具体来说,你当前的selector是:

x => x.GetType().GetProperty(filter.ColumnProperty).GetValue(x)

这本质是在表达式树中嵌入了GetType()、GetProperty()、GetValue()三个反射方法的调用,而非直接生成x.Year这样的属性访问节点。当LINQ执行这个表达式时,会存在以下问题:

  • GetValue()返回object类型,导致Compare方法的TValue被推断为object,而你的filter.ColumnValue如果是值类型(比如int),会被装箱,虽然值相等,但表达式树的类型匹配逻辑可能出现隐性问题;
  • 反射调用的表达式无法被LINQ提供者(即使是内存中的LINQ to Objects)优化,且容易出现类型转换的隐性错误;
  • 若filter.ColumnProperty拼写错误,GetProperty()返回null,后续GetValue()会直接抛出空引用异常。
修复方案

需要动态构建属性访问的表达式树,替代反射方法调用。具体步骤如下:

1. 扩展Compare方法,添加动态属性版本

public static IQueryable<TSource> Compare<TSource>(
    this IQueryable<TSource> source,
    string propertyName,
    object value,
    ExpressionType comparison)
{
    // 构建参数表达式:x => x
    var parameter = Expression.Parameter(typeof(TSource), "x");
    // 构建属性访问表达式:x.PropertyName
    var property = Expression.Property(parameter, propertyName);
    // 将值转换为属性的类型(处理装箱/拆箱)
    var convertedValue = Expression.Constant(Convert.ChangeType(value, property.Type), property.Type);
    
    BinaryExpression body;
    switch (comparison)
    {
        case ExpressionType.LessThan:
            body = Expression.LessThan(property, convertedValue);
            break;
        case ExpressionType.LessThanOrEqual:
            body = Expression.LessThanOrEqual(property, convertedValue);
            break;
        case ExpressionType.Equal:
            body = Expression.Equal(property, convertedValue);
            break;
        case ExpressionType.NotEqual:
            body = Expression.NotEqual(property, convertedValue);
            break;
        case ExpressionType.GreaterThan:
            body = Expression.GreaterThan(property, convertedValue);
            break;
        case ExpressionType.GreaterThanOrEqual:
            body = Expression.GreaterThanOrEqual(property, convertedValue);
            break;
        case ExpressionType.IsTrue:
            // 处理"is empty"逻辑:检查属性是否为null或默认值
            body = Expression.Equal(property, Expression.Default(property.Type));
            break;
        case ExpressionType.IsFalse:
            // 处理"is not empty"逻辑:检查属性是否不为null且不是默认值
            body = Expression.NotEqual(property, Expression.Default(property.Type));
            break;
        default:
            throw new NotSupportedException($"Comparison operator {comparison} is not supported");
    }

    // 构建Lambda表达式并应用Where过滤
    var lambda = Expression.Lambda<Func<TSource, bool>>(body, parameter);
    return source.Where(lambda);
}

2. 修改Filter方法,使用新的Compare重载

public Task<IEnumerable<T>> Filter<T>(IList<DataTableFilter> filters, IEnumerable<T> filterables)
{
    if (filters == null || filters.Count == 0)
        return Task.FromResult(filterables);

    var queryable = filterables.AsQueryable();
    foreach (var filter in filters)
    {
        if (string.IsNullOrEmpty(filter.ColumnProperty))
            continue;

        queryable = queryable.Compare(filter.ColumnProperty, filter.ColumnValue, filter.FilterOperator);
    }

    return Task.FromResult(queryable.AsEnumerable());
}

3. 优化DataTableFilter的FilterOperator属性

补充is empty和is not empty的逻辑适配:

public record DataTableFilter
{
    public string? ColumnProperty { get; set; }
    public object? ColumnValue { get; set; }
    public string? Operator { get; set; }

    public ExpressionType FilterOperator
    {
        get
        {
            return Operator switch
            {
                "=" => ExpressionType.Equal,
                "!=" => ExpressionType.NotEqual,
                ">" => ExpressionType.GreaterThan,
                ">=" => ExpressionType.GreaterThanOrEqual,
                "<" => ExpressionType.LessThan,
                "<=" => ExpressionType.LessThanOrEqual,
                "is empty" => ExpressionType.IsTrue,
                "is not empty" => ExpressionType.IsFalse,
                _ => ExpressionType.Throw
            };
        }
    }
}
测试验证

修改后,当你测试Year=2024的过滤条件时,表达式树会直接生成x => x.Year == 2024这样的逻辑,类型匹配正确,过滤结果会符合预期。

内容的提问来源于stack exchange,提问作者B-M

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 00:25:55