如何通过EF.Functions.Contains实现多属性SQL Server全文搜索
EF Core调用SQL Server全文搜索CONTAINS实现多列检索
问题背景
单列使用EF.Functions.Contains调用SQL Server全文搜索功能正常,代码示例:
var test = await dbContext.MyTable.Where(x => EF.Functions.Contains(x.Column1, searchText)).ToListAsync();
对应的SQL:
select * from MyTable where contains(Column1 , N'SearchText')
但需要生成多列检索的SQL:
select * from MyTable where contains((Column1, Column2) , N'SearchText')
之前尝试的两种写法均无效:
- 写法1:传入Expression数组,EF无法识别
Expression<Func<MyTable, object>>[] propertyReferences = { x => x.Column1 , x=> x.Column2 }; var test = await dbContext.MyTable.Where(x => EF.Functions.Contains(propertyReferences, searchText)).ToListAsync();
- 写法2:使用匿名类型,低版本EF Core不支持
var test = await dbContext.MyTable.Where(x => EF.Functions.Contains(new {x.Column1 , x.Column2 }, searchText)).ToListAsync();
解决方案
根据你的EF Core版本,选择对应的实现方式:
1. EF Core 5.0及以上版本
EF Core 5+新增了支持多列的Contains重载,直接使用匿名类型即可生成目标SQL:
var searchText = "你的搜索关键词"; var test = await dbContext.MyTable .Where(x => EF.Functions.Contains(new { x.Column1, x.Column2 }, searchText)) .ToListAsync();
注意:确保你的数据库表已经为Column1和Column2创建了组合全文索引,否则SQL Server会报错。
2. EF Core 3.x及以下版本
低版本EF Core没有多列Contains重载,可通过以下两种方式实现:
方法一:原生SQL查询
直接编写原生SQL语句,简单直接:
var test = await dbContext.MyTable .FromSqlRaw("SELECT * FROM MyTable WHERE CONTAINS((Column1, Column2), {0})", searchText) .ToListAsync();
方法二:构造表达式树(动态多列场景)
如果需要动态指定检索列,可以创建扩展方法来生成对应的表达式:
public static class QueryableExtensions { public static IQueryable<T> WhereContains<T>(this IQueryable<T> query, string searchText, params Expression<Func<T, object>>[] propertySelectors) { if (propertySelectors.Length == 0 || string.IsNullOrEmpty(searchText)) return query; // 处理每个属性表达式,去除不必要的Convert包装 var columnExpressions = propertySelectors.Select(selector => { var body = selector.Body; if (body is UnaryExpression unaryExpr && unaryExpr.NodeType == ExpressionType.Convert) body = unaryExpr.Operand; return body; }).ToList(); // 构造匿名类型实例表达式(对应SQL中的(Column1, Column2)) var anonymousTypeExpr = Expression.New( typeof(object).GetConstructor(Type.EmptyTypes), columnExpressions, Array.Empty<MemberInfo>() ); // 获取EF.Functions.Contains方法的引用 var containsMethod = typeof(DbFunctionsExtensions) .GetMethod(nameof(DbFunctionsExtensions.Contains), new[] { typeof(DbFunctions), typeof(object), typeof(string) }); // 构造方法调用表达式 var dbFunctionsExpr = Expression.Property(null, typeof(EF).GetProperty(nameof(EF.Functions))); var searchTextExpr = Expression.Constant(searchText); var containsCallExpr = Expression.Call(containsMethod, dbFunctionsExpr, anonymousTypeExpr, searchTextExpr); // 构造Where的Lambda表达式 var parameterExpr = Expression.Parameter(typeof(T)); var lambdaExpr = Expression.Lambda<Func<T, bool>>(containsCallExpr, parameterExpr); return query.Where(lambdaExpr); } }
使用方式:
var test = await dbContext.MyTable .WhereContains(searchText, x => x.Column1, x => x.Column2) .ToListAsync();
无效写法原因说明
- 写法1:
EF.Functions.Contains没有接受Expression<Func<T,object>>[]类型参数的重载,EF无法将其转换为对应的SQL语法。 - 写法2:EF Core 3.x及以下版本不存在接受匿名类型的
Contains重载,因此无法识别并转换为多列CONTAINS语法。
内容的提问来源于stack exchange,提问作者Moseyza
相关产品推荐
相关产品推荐

