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

EF Core 9调用List<int>的Contains()时生成无效SQL,提示'$'附近语法不正确

EF Core 9调用List的Contains()时生成无效SQL,提示'$'附近语法不正确

嘿,我来帮你梳理下这个问题——这不是EF Core的bug,而是EF Core 9引入的新查询优化策略,只是在特定环境下触发了兼容性问题,咱们一步步拆解:

为什么会生成OPENJSON的SQL?

EF Core 9针对值类型列表(比如List<int>、int[]这类整数/数值类型集合)的Contains查询,默认启用了一个性能优化:不再像旧版本那样把列表拆成IN (1,2,3)这种多参数SQL,而是将整个列表序列化为JSON,用OPENJSON来解析查询。这么做的好处是当列表元素很多时,能避免SQL Server因参数过多导致的解析性能下降。

但问题出在生成的OPENJSON(@__ids_0) WITH ([value] int '$')语法上:这个写法依赖SQL Server 2016及以上版本(或者Azure SQL Database),同时要求数据库的兼容性级别至少为130。如果你的数据库版本不够,或者兼容性级别没达标,就会抛出语法错误。

至于字符串列表的Contains没问题,是因为EF Core目前对字符串集合的Contains还沿用传统的IN子句逻辑,没有启用这个OPENJSON优化,所以不会触发这个问题。

解决办法

这里有几个可行的方案,你可以根据自己的环境选择:

1. 禁用OPENJSON优化,回退到传统IN子句

如果你暂时不想升级数据库,可以在DbContext的配置里关闭这个优化:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(
        "你的数据库连接字符串",
        sqlOptions => sqlOptions.DisableOpenJsonForListContains());
}

这样EF Core就会回到旧的处理方式,生成IN (@p0, @p1, ...)的SQL,不会再用OPENJSON。

2. 升级数据库或调整兼容性级别

如果你的数据库是SQL Server 2016及以上版本,先检查兼容性级别:
执行以下SQL查询:

SELECT name, compatibility_level 
FROM sys.databases 
WHERE name = '你的数据库名称';

如果返回的compatibility_level低于130,执行下面的语句调整:

ALTER DATABASE 你的数据库名称 
SET COMPATIBILITY_LEVEL = 130;

调整后,OPENJSON的语法就能正常被数据库识别了。

3. 临时 workaround:手动转换列表(不推荐,仅应急可用)

如果你不想改配置也不想动数据库,可以把List<int>转换成List<string>再用Contains?不过这需要你的实体Id字段也转成字符串比较,会影响性能,只适合临时应急:

var stringIds = ids.Select(id => id.ToString()).ToList();
return await _dbContext.Entities
    .Include(e => e.RelatedEntities)
    .Where(e => stringIds.Contains(e.Id.ToString()))
    .ToListAsync();

这个方法不推荐长期使用,毕竟会导致索引失效,性能下降。

总结

这个问题本质是EF Core 9的新优化和旧数据库环境的兼容性冲突,要么禁用优化,要么升级/调整数据库配置就能解决啦。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 09:03:01