如何解决EF Core中组合EF.Functions.Like与.Any出现表达式无法翻译的问题
EF Core 多搜索词模糊匹配查询解决方案
EF Core 原生不支持本地集合结合Any + EF.Functions.Like的表达式翻译,以下是可直接落地的实现方案:
方案1:原生表达式树动态拼接OR条件(无第三方依赖)
该方案会将多个模糊匹配条件通过OR拼接,最终生成的SQL形式为WHERE Name LIKE N'%red%' OR Name LIKE N'%blue%',适用于搜索词数量较少(一般100个以内)的场景:
var searchTerms = new[] { "%red%", "%blue%", /* 数量未知的搜索词 */ }; var widgets = Array.Empty<Widget>(); if (searchTerms.Any()) { // 构建表达式参数 var parameter = Expression.Parameter(typeof(Widget), "w"); // 取实体Name属性表达式 var nameProperty = Expression.Property(parameter, nameof(Widget.Name)); // 获取EF.Functions.Like方法元数据 var likeMethod = typeof(DbFunctionsExtensions) .GetMethod(nameof(DbFunctionsExtensions.Like), new[] { typeof(DbFunctions), typeof(string), typeof(string) }); // 循环拼接所有Like条件 Expression? conditionBody = null; foreach (var term in searchTerms) { var currentLike = Expression.Call( likeMethod!, Expression.Constant(EF.Functions), nameProperty, Expression.Constant(term) ); conditionBody = conditionBody == null ? currentLike : Expression.OrElse(conditionBody, currentLike); } // 生成查询委托并执行 var filter = Expression.Lambda<Func<Widget, bool>>(conditionBody!, parameter); widgets = await Widgets.Where(filter).ToArrayAsync(); } else { // 无搜索词时的业务逻辑,可根据需求调整为返回全量/空集合 widgets = await Widgets.ToArrayAsync(); }
方案2:使用LinqKit简化表达式拼接
如果允许引入第三方包,可以用LinqKit的PredicateBuilder减少样板代码,实现逻辑和方案1完全一致:
// 需先安装NuGet包 LinqKit.Microsoft.EntityFrameworkCore using LinqKit; // ... var searchTerms = new[] { "%red%", "%blue%" }; var predicate = PredicateBuilder.New<Widget>(); foreach (var term in searchTerms) { predicate = predicate.Or(w => EF.Functions.Like(w.Name, term)); } var widgets = await Widgets.Where(predicate).ToArrayAsync();
方案3:临时表关联查询(搜索词量大/EF Core 7+ 场景)
如果搜索词数量超过100个,或者需要生成你期望的EXISTS查询结构,可以通过临时表存储搜索词再关联查询:
- 先定义无键实体用于映射临时表字段
public class SearchTerm { public string Term { get; set; } }
- 在DbContext的OnModelCreating中配置无键实体
modelBuilder.Entity<SearchTerm>().HasNoKey();
- 执行查询
var searchTerms = new[] { "%red%", "%blue%" }; // 创建临时表并写入搜索词 await dbContext.Database.ExecuteSqlInterpolatedAsync($"CREATE TABLE #SearchTerms (Term NVARCHAR(200) NOT NULL)"); foreach (var term in searchTerms) { await dbContext.Database.ExecuteSqlInterpolatedAsync($"INSERT INTO #SearchTerms VALUES ({term})"); } // 关联查询,生成的SQL和你期望的EXISTS结构完全一致 var widgets = await Widgets .Where(w => dbContext.Set<SearchTerm>() .FromSqlRaw("SELECT Term FROM #SearchTerms") .Any(t => EF.Functions.Like(w.Name, t.Term)) ) .ToArrayAsync(); // 可选:用完删除临时表 await dbContext.Database.ExecuteSqlRawAsync("DROP TABLE #SearchTerms");
内容的提问来源于stack exchange,提问作者moswald
相关产品推荐
相关产品推荐

