如何使用Linq的Where条件检查字符串列表包含项,解决表达式无法转换报错
错误原因
你遇到的报错是EF Core无法将投影后内存集合RoleNames上的Any()操作转换为SQL语句。你在IQueryable阶段调用了ToList()将角色列表内存化,后续针对该内存集合的过滤逻辑无法被EF Core的查询翻译器识别。
推荐解决方案
将过滤逻辑提前到查询执行前,基于数据库表结构编写可被翻译的过滤条件,过滤完成后再做字段投影。如果你的实体配置了User到UserRole、UserRole到Role的导航属性,可以用更简洁的写法:
// 先构造基础查询 IQueryable<User> query = _context.Users; // 处理过滤逻辑 if (!string.IsNullOrEmpty(searchText)) { query = query.Where(user => // 匹配用户名 user.UserName.Contains(searchText) // 匹配用户关联的任意角色名 || user.UserRoles.Any(ur => ur.Role.Name.Contains(searchText))); } // 过滤完成后投影需要的字段 var data = query.Select(user => new { UserId = user.Id, Username = user.UserName, RoleNames = user.UserRoles.Select(ur => ur.Role.Name).ToList() });
如果没有配置导航属性,可以用Join写法实现相同逻辑:
var data = from user in _context.Users where string.IsNullOrEmpty(searchText) || user.UserName.Contains(searchText) // 子查询匹配关联角色 || (from ur in _context.UserRoles join r in _context.Roles on ur.RoleId equals r.Id where ur.UserId == user.Id select r.Name).Any(r => r.Contains(searchText)) select new { UserId = user.Id, Username = user.UserName, RoleNames = (from ur in _context.UserRoles join r in _context.Roles on ur.RoleId equals r.Id where ur.UserId == user.Id select r.Name).ToList() };
以上两种写法的过滤逻辑都会被EF Core翻译为SQL在数据库侧执行,执行效率高,完全符合你的模糊匹配需求。
不推荐的备选方案(仅适合极小数据量场景)
如果业务场景用户量极少,也可以先将全量数据加载到内存再执行过滤,不需要调整过滤逻辑:
// 调用AsEnumerable()切换为LINQ to Objects,后续操作在内存执行 var data = (from user in _context.Users select new { UserId = user.Id, Username = user.UserName, RoleNames = (from userRole in _context.UserRoles join role in _context.Roles on userRole.RoleId equals role.Id where userRole.UserId == user.Id select role.Name).ToList() }).AsEnumerable(); if (!string.IsNullOrEmpty(searchText)) data = data.Where(x => x.Username.Contains(searchText) || x.RoleNames.Any(r => r.Contains(searchText)));
该方案会先加载所有用户和角色数据到内存,数据量大时会存在严重性能问题,不建议在生产环境使用。
内容的提问来源于stack exchange,提问作者Pendi Hu
相关产品推荐
相关产品推荐

