EF Core中如何构建属性动态选择的Where谓词
问题解决:EF Core中表达式树无法翻译Invoke的问题
问题原因
你原来的方法接收Func<E, NpgsqlTsVector>类型的参数,返回的表达式树里会生成Invoke节点,EF Core无法将这种委托调用转换为对应的SQL语句,因此抛出无法翻译的错误。
解决方案
需要直接基于表达式树拼接调用逻辑,而非依赖委托调用。下面提供两种实现方式:
方式一:接收表达式lambda(推荐,调用更直观)
修改方法签名为接收Expression<Func<E, NpgsqlTsVector>>,手动构建调用Matches方法的表达式树:
using System.Linq.Expressions; using NpgsqlTypes; public Expression<Func<E, bool>> GetWherePredicate<E>(Expression<Func<E, NpgsqlTsVector>> selector, string queryText) { // 获取实体参数(对应lambda里的entity) ParameterExpression entityParam = selector.Parameters[0]; // 获取selector的表达式体(比如i => i.MySearchField中的i.MySearchField) Expression tsVectorExpression = selector.Body; // 获取NpgsqlTsVector的Matches方法(参数为string的重载) MethodInfo matchesMethod = typeof(NpgsqlTsVector).GetMethod( nameof(NpgsqlTsVector.Matches), new[] { typeof(string) })!; // 构建调用Matches的表达式:tsVectorExpression.Matches(queryText) MethodCallExpression matchesCall = Expression.Call( tsVectorExpression, matchesMethod, Expression.Constant(queryText)); // 最终构建返回的lambda表达式:entity => entity.MySearchField.Matches(queryText) return Expression.Lambda<Func<E, bool>>(matchesCall, entityParam); }
调用方式和你原来的完全一致:
query = query.Where(GetWherePredicate<MyEntity>(i => i.MySearchField, "the_query"));
方式二:接收MemberExpression(按你需求的签名)
如果坚持使用MemberExpression作为参数,实现如下:
using System.Linq.Expressions; using NpgsqlTypes; public Expression<Func<E, bool>> GetWherePredicate<E>(MemberExpression selectorForSearchField, string queryText) { // 校验传入的MemberExpression主体是E类型参数 if (!(selectorForSearchField.Expression is ParameterExpression entityParam) || entityParam.Type != typeof(E)) { throw new ArgumentException("selectorForSearchField必须是E类型参数的成员表达式"); } // 获取Matches方法 MethodInfo matchesMethod = typeof(NpgsqlTsVector).GetMethod( nameof(NpgsqlTsVector.Matches), new[] { typeof(string) })!; // 构建调用Matches的表达式 MethodCallExpression matchesCall = Expression.Call( selectorForSearchField, matchesMethod, Expression.Constant(queryText)); // 构建返回的lambda return Expression.Lambda<Func<E, bool>>(matchesCall, entityParam); }
调用时需手动构建MemberExpression(不如方式一便捷):
ParameterExpression param = Expression.Parameter(typeof(MyEntity), "i"); MemberExpression memberExpr = Expression.PropertyOrField(param, nameof(MyEntity.MySearchField)); query = query.Where(GetWherePredicate<MyEntity>(memberExpr, "the_query"));
原理说明
通过直接拼接表达式树节点,EF Core可以完整解析MySearchField.Matches(queryText)的逻辑,将其转换为PostgreSQL对应的全文检索SQL,避免了无法翻译的Invoke节点。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

