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

如何通过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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 14:53:16