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

使用LinqKit构建的表达式无法被EF Core翻译,求解决方案

问题描述

我们采用通用方式用LinqKit构建表达式,处理内存List时一切正常,但使用EF Core查询数据库时出现错误。

错误信息

System.InvalidOperationException: The LINQ expression 'DbSet()
.Where(k => False || __propertyInfo_0.GetValue(k) != null && __propertyInfo_0.GetValue(k).ToString().Contains(__GenericTableOptions_BaseQueryParameters_Search_1) && True)' could not be translated.

提示:请将查询改写为可翻译的形式,或通过调用AsEnumerable、AsAsyncEnumerable、ToList或ToListAsync显式切换到客户端求值。

泛型表达式构建器代码

private Expression<Func<TEntity, bool>> BuildGenericSearchExpression()
{
    Expression<Func<TEntity, bool>> expression = x => false;

    var searchFields = GenericTableOptions.SearchColumns;
    var propertyInfos = GetColumnsAsProperties(typeof(TEntity), searchFields).ToList();

    if (propertyInfos.Count == 0)
    {
        // Returning true to allow AND chaining of multiple conditions
        return x => true;
    }

    foreach (var propertyInfo in propertyInfos)
    {
        expression = expression.Or(x =>
            propertyInfo.GetValue(x) != null &&
            propertyInfo.GetValue(x)!.ToString()!.Contains(GenericTableOptions.BaseQueryParameters.Search));
    }

    return expression;
}
解决方案

问题根源是直接使用PropertyInfo.GetValue()和ToString()这类反射调用,EF Core无法将其翻译成SQL语句。必须手动构建表达式树来实现属性访问和方法调用,替代反射逻辑。

修改后的代码如下:

using System.Linq.Expressions;

private Expression<Func<TEntity, bool>> BuildGenericSearchExpression()
{
    var searchValue = GenericTableOptions.BaseQueryParameters.Search;
    if (string.IsNullOrEmpty(searchValue))
    {
        return x => true;
    }

    var searchFields = GenericTableOptions.SearchColumns;
    var propertyInfos = GetColumnsAsProperties(typeof(TEntity), searchFields).ToList();

    if (propertyInfos.Count == 0)
    {
        return x => true;
    }

    Expression<Func<TEntity, bool>> expression = x => false;

    foreach (var propertyInfo in propertyInfos)
    {
        // 构建参数表达式:x
        var parameter = Expression.Parameter(typeof(TEntity), "x");
        // 构建属性访问表达式:x.PropertyName
        var propertyAccess = Expression.Property(parameter, propertyInfo);
        // 构建null检查:x.PropertyName != null
        var nullCheck = Expression.NotEqual(propertyAccess, Expression.Constant(null, propertyInfo.PropertyType));

        // 处理属性转字符串:字符串类型直接使用,非字符串类型调用ToString()
        Expression stringValue;
        if (propertyInfo.PropertyType == typeof(string))
        {
            stringValue = propertyAccess;
        }
        else
        {
            var toStringMethod = typeof(object).GetMethod("ToString", Type.EmptyTypes)!;
            stringValue = Expression.Call(propertyAccess, toStringMethod);
        }

        // 构建Contains调用:stringValue.Contains(searchValue)
        var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) })!;
        var containsCall = Expression.Call(stringValue, containsMethod, Expression.Constant(searchValue));

        // 合并null检查与Contains条件
        var condition = Expression.AndAlso(nullCheck, containsCall);

        // 转换为Lambda表达式
        var lambda = Expression.Lambda<Func<TEntity, bool>>(condition, parameter);

        // 用LinqKit的Or合并表达式
        expression = expression.Or(lambda);
    }

    return expression;
}

关键修改点

  • 用Expression.Property替代反射的GetValue(),让EF Core能识别属性并翻译成SQL列引用
  • 手动构建ToString()和Contains的方法调用表达式,确保EF Core可以将这些操作翻译成SQL
  • 提前提取搜索值,避免循环中重复引用外部变量导致的表达式树异常

另外,若属性为值类型(如int、DateTime),可跳过null检查逻辑,减少多余的SQL条件。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 02:09:09