如何用EF和LINQ筛选名称包含数组中字符串的记录?
解决EF Core LINQ表达式无法翻译的问题
问题原因
EF Core无法将strArray.Any(s => i.Name.Contains(s))这类嵌套表达式正确翻译成SQL语句——内存数组的Any操作结合实体属性的Contains方法,超出了EF Core默认的表达式翻译能力范围。
解决方案:手动构建OR过滤条件
通过表达式树拼接多个Name.Contains(s)的OR条件,让EF Core能正确生成对应的SQL LIKE查询。
步骤1:实现简单的PredicateBuilder辅助类
这个类用来拼接多个过滤条件:
public static class PredicateBuilder { public static Expression<Func<T, bool>> False<T>() => _ => false; public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right) { var invokedRight = Expression.Invoke(right, left.Parameters.Cast<Expression>()); return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left.Body, invokedRight), left.Parameters); } }
步骤2:修改查询代码
在strArray不为空的分支中,用PredicateBuilder构建OR条件:
[HttpGet] public async Task<IActionResult> GetContent([FromQuery] string[]? strArray) { List<DynamicContentDTO> dynContentList; if (strArray == null) { dynContentList = await _context.DynamicContents .OrderBy(x => x.OrderIndex) .Select(d => new DynamicContentDTO(d)) .ToListAsync(); } else { var predicate = PredicateBuilder.False<DynamicContent>(); foreach (var s in strArray) { var searchTerm = s; // 避免闭包捕获变量的问题 predicate = predicate.Or(content => content.Name.Contains(searchTerm)); } dynContentList = await _context.DynamicContents .OrderBy(x => x.OrderIndex) .Where(predicate) .Select(d => new DynamicContentDTO(d)) .ToListAsync(); } return Ok(dynContentList); }
替代方案(EF Core 5.0+适用)
如果你使用的是EF Core 5.0及以上版本,可以尝试使用EF.Functions.Like替代Contains,部分场景下EF Core能直接翻译:
.Where(i => strArray.Any(s => EF.Functions.Like(i.Name, $"%{s}%")))
但手动构建OR条件的方式兼容性更强,适用于所有EF Core版本。
内容的提问来源于stack exchange,提问作者Dzenis Zigo
相关产品推荐
相关产品推荐

