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

Linq技术咨询:如何结合反射实现含Contains的动态Where查询

动态Contains筛选的实现方案

我明白你想把硬编码的Description属性筛选逻辑改成动态可配置的,这样就能灵活针对任意字符串属性执行Contains过滤了。下面是基于表达式树的最佳实践方案——它既能被EF Core正确解析为SQL查询,又能保持代码的可扩展性:

完整修改后的代码

using System.Linq.Expressions;
using System.Globalization;

public List<TEntity> GetItems(string sortBy, string sort, string? filter, string filterPropertyName = "Description")
{
    IQueryable<TEntity> items = dbContext.Set<TEntity>();

    if (!string.IsNullOrEmpty(filter))
    {
        string lowerFilter = filter.ToLower(CultureInfo.CurrentCulture);
        var filterExpression = BuildContainsFilterExpression<TEntity>(filterPropertyName, lowerFilter);
        items = items.Where(filterExpression);
    }

    if (!string.IsNullOrEmpty(sortBy))
    {
        items = items.OrderBy($"{sortBy} {sort}");
    }

    return items.ToList();
}

// 动态构建Contains筛选的表达式树
private Expression<Func<TEntity, bool>> BuildContainsFilterExpression<T>(string propertyName, string lowerFilter)
{
    // 参数表达式:p => ...
    var parameter = Expression.Parameter(typeof(T), "p");
    
    // 获取属性:p.[指定属性名]
    var property = Expression.Property(parameter, propertyName);
    
    // 处理null检查:p.[指定属性名] != null
    var nullCheck = Expression.NotEqual(property, Expression.Constant(null, typeof(string)));
    
    // 调用ToLower():p.[指定属性名].ToLower(当前文化)
    var toLowerMethod = typeof(string).GetMethod("ToLower", new[] { typeof(CultureInfo) })!;
    var toLowerCall = Expression.Call(
        property, 
        toLowerMethod, 
        Expression.Constant(CultureInfo.CurrentCulture)
    );
    
    // 调用Contains():p.[指定属性名].ToLower().Contains(过滤值)
    var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) })!;
    var containsCall = Expression.Call(
        toLowerCall, 
        containsMethod, 
        Expression.Constant(lowerFilter, typeof(string))
    );
    
    // 组合条件:null检查 && Contains匹配
    var combinedCondition = Expression.AndAlso(nullCheck, containsCall);
    
    // 构建最终表达式并返回
    return Expression.Lambda<Func<T, bool>>(combinedCondition, parameter);
}

关键细节解释

  • 表达式树的必要性:EF Core需要解析表达式树来生成对应的SQL语句,如果直接用反射调用方法(比如property.GetValue(p).ToString().Contains(...)),会导致内存中过滤(客户端评估),性能极差。表达式树能让EF Core把整个筛选逻辑翻译成SQL,在数据库层面执行。
  • 属性合法性校验:如果需要更严谨,可以在BuildContainsFilterExpression方法开头加属性存在性检查,避免传入不存在的属性名:
    if (!typeof(T).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)?.PropertyType.IsAssignableTo(typeof(string)) ?? false)
    {
        throw new ArgumentException($"类型{typeof(T).Name}不存在可访问的字符串属性{propertyName}");
    }
    
  • 灵活性扩展:你可以进一步封装,比如支持传入多个属性名做OR筛选,或者让用户指定是否忽略大小写(不过你的当前逻辑已经处理了忽略大小写)。

使用示例

  • 默认筛选Description属性:
    var items = GetItems("Id", "ASC", "test filter");
    
  • 动态指定其他属性(比如Name):
    var items = GetItems("Id", "ASC", "test filter", "Name");
    

内容的提问来源于stack exchange,提问作者Henning

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 07:59:11