使用EF.Functions.Like查询数组类型时EF Core抛出异常求助
解决EF Core 3.1 + Npgsql中数组Like查询无法翻译的问题
看起来你碰到了EF Core查询翻译的经典问题——你的LINQ表达式没办法被转换成PostgreSQL能理解的SQL,这在EF Core 3.x版本里挺常见的,尤其是针对数组字段的嵌套函数调用。咱们一步步来解决:
问题根源
EF Core 3.1的Npgsql提供者还不支持把EF.Functions.Like嵌套在Any里针对数组的操作翻译成对应的PostgreSQL语法。PostgreSQL对数组的模式匹配有自己的一套写法,但EF Core当时还没适配这种场景。
可行的解决办法
1. 用PostgreSQL原生数组函数直接实现(推荐)
咱们可以利用PostgreSQL的unnest函数把数组拆成行,再做Like匹配,用原生SQL的方式来写查询,这样EF Core就不用费劲翻译了:
var searchPattern = $"%{searchFile}%"; getFiles = await _context.Files .Include(r => r.Box) .FromSqlRaw(@" SELECT f.* FROM ""Files"" f JOIN ""Boxes"" b ON f.""BoxId"" = b.""Id"" WHERE b.""AccountId"" = {0} AND EXISTS ( SELECT 1 FROM unnest(f.""SealCodes"") sc WHERE sc LIKE {1} ) ", accountId, searchPattern) .OrderByDescending(c => c.CreatedOn) .ToListAsync();
或者更简洁一点,用array_to_string把数组转成字符串再做Like匹配(注意如果你的数组元素里有分隔符可能会有误匹配,选分隔符的时候要避开业务里的字符):
getFiles = await _context.Files.Include(r => r.Box) .Where(r => r.Box.AccountId == accountId && EF.Functions.Like(EF.Functions.ArrayToString(r.SealCodes, "||"), $"%{searchFile}%")) .OrderByDescending(c => c.CreatedOn) .ToListAsync();
2. 强制客户端评估(适合小数据量场景)
如果你的数据量不大,不想折腾原生SQL,可以先把符合AccountId条件的数据拉到内存里,再在客户端做过滤。但要注意,数据量大的话会严重影响性能:
getFiles = await _context.Files.Include(r => r.Box) .Where(r => r.Box.AccountId == accountId) .AsEnumerable() // 这里切换到客户端处理 .Where(r => r.SealCodes.Any(s => s.Contains(searchFile))) // 客户端用Contains模拟Like的部分匹配 .OrderByDescending(c => c.CreatedOn) .ToListAsync();
3. 升级EF Core和Npgsql版本(长期最优解)
EF Core 3.1已经是比较老的版本了,后续的EF Core 5.0+版本对PostgreSQL数组的支持有了很大提升,很多之前无法翻译的表达式都能正确处理。如果你能升级到Npgsql.EntityFrameworkCore.PostgreSQL 5.x或更高版本,你的原代码大概率就能直接正常运行,不用改逻辑。
小提示
- 如果你需要不区分大小写的匹配,把
Like换成ILike就行,PostgreSQL的ILike是原生支持不区分大小写的。 - 用原生SQL的时候一定要用参数占位符(比如上面的
{0}、{1}),别直接拼接字符串,避免SQL注入风险。
内容的提问来源于stack exchange,提问作者Azri Zakaria
相关产品推荐
相关产品推荐

