使用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
相关产品推荐
相关产品推荐

