EF Core 8 + SQL Server:Contains() 突然报 “关键字 WITH 附近有语法错误“?一篇避坑指南

EF Core 8 + SQL Server:Contains() 突然报 “关键字 WITH 附近有语法错误“? EF Core 8 的项目,所有Contains都要在心里打个问号— 写的时候就要想好万一报错走哪个方案List<int?比List<int>更容易触发— 可空类型让 EF 生成的 SQL 更复杂,更倾向 CTESQL Server 错误 156 看到WITH就看Contains— 排查方向比错误信息本身更重要记录到 MEMORY.md— 团队其他人可能不知道这个坑,要有文档沉淀一句话总结:EF Core 8 把Contains。 阅读详情

大家好,我是码农刚子,最近遇到一个EF Core 查询的问题,想跟大家分享下,应该有很多小伙伴已经遇到过了。

升级到 .NET 8 / EF Core 8 后,原来跑得好好的 Where(x => ids.Contains(x.id)) 突然炸了,日志里赫然写着:关键字 'WITH' 附近有语法错误。如果此语句是公用表表达式,那么前一个语句必须以分号结尾。 ——这篇文章帮你搞清楚为什么、怎么修、以后怎么写才不踩坑。

1. 先看症状

假设你有这样一段再普通不过的代码:

var ids = new List<int> { 1, 2, 3, 5, 8 };
var users = await _db.sys_admin
    .Where(x => ids.Contains(x.id))
    .ToListAsync();

在 EF Core 6/7 上完全正常。升级到 EF Core 8 后,同样的代码报:

Microsoft.Data.SqlClient.SqlException (0x80131904):
关键字 'WITH' 附近有语法错误。
如果此语句是公用表表达式、xmlnamespaces 子句或者更改跟踪上下文子句,
那么前一个语句必须以分号结尾。

关键词:WITH、分号、公用表表达式(CTE)。SQL Server 错误号为 156。

2. 根因:EF Core 8 对 Contains 的翻译方式变了

这不是 Bug,这是 EF Core 8 的一个 有意为之的 Breaking Change(官方文档明确定义为 High Impact)。

2.1 旧行为(EF Core 6/7)

EF 把参数化列表的值内联为 SQL 常量:

-- EF Core 7 生成的 SQL
SELECT [s].[id], [s].[username], ...
FROM [sys_admin] AS [s]
WHERE [s].[id] IN (1, 2, 3, 5, 8)

简单直接,没有 CTE,没有任何问题——直到你开始关注查询计划缓存。

2.2 新行为(EF Core 8)

EF Core 8 不再内联常量,而是通过 OPENJSON 或 CTE(公用表表达式) 来传递参数化集合。简化后的生成逻辑是:

简单值列表(string/int 常量)→ OPENJSON 方式
复杂查询 / 多次 Contains → CTE(WITH ... AS)方式

对于 ids.Contains(x.id) 这类场景,EF 可能生成类似这样的 SQL:

-- EF Core 8 可能生成的 SQL(简化版)
;WITH [t] AS (
    SELECT [v].[value] FROM OPENJSON(@__ids_0) ...
)
SELECT [s].[id], ...
FROM [sys_admin] AS [s]
WHERE [s].[id] IN (SELECT [t].[value] FROM [t])

问题来了:WITH 前面必须有一个完整语句的分号 ;。如果当前 SQL 批处理中 EF 没有在前面补上分号,SQL Server 就会报错 156。

2.3 官方文档怎么说

微软在 EF Core 8 Breaking Changes 中明确记录了这条(Tracking Issue #13617):

Contains in LINQ queries may stop working on older SQL Server versions

Impact: High

链接:learn.microsoft.com - Breaking changes in EF Core 8.0

3. 什么时候会触发?

不是所有 Contains 都会炸,但它会在你不经意间冒出来。触发条件包括但不限于:

场景风险
ids.Contains(x.id) 且 ids 是 List<int>🔴 高
ids.Contains(x.id) 且 ids 是 List<int?>🔴 高(我们项目遇到的)
stringList.Contains(x.name)🟡 中(可能走 OPENJSON)
同一查询中有多个 Contains🔴 高
.Contains() 嵌套在复杂 Where 表达式中🟡 中
查询中同时有其他关联(Join / Include)🔴 高

最重要的信号:一旦看到错误信息里出现 WITH 和 分号,99% 就是这个问题。

4. 解决方案(5 种,从优到差)

方案一:参数化 Raw SQL(推荐 ⭐)

直接绕过 EF 翻译,用 FromSqlRaw + SqlParameter,性能最优,零坑:

var paramNames = ids.Select((_, i) => $"@p{i}").ToArray();
var parameters = ids.Select((id, i) => 
    new Microsoft.Data.SqlClient.SqlParameter($"@p{i}", id)).ToArray();
var sql = $"SELECT * FROM sys_admin WHERE id IN ({string.Join(",", paramNames)})";

var users = await _db.sys_admin
    .FromSqlRaw(sql, parameters)
    .ToListAsync();
  • ✅ 生成的 SQL 就是简单的 WHERE id IN (@p0, @p1, ...)
  • ✅ Microsoft.Data.SqlClient 随 EF Core SQL Server 包引入,无需另装
  • ✅ 完全防注入
  • ⚠️ 需要知道表名(但你的 DbContext 本来就定义了)

适用:批量删除、批量更新、批量查询等「已知 ID 列表查实体」场景。

方案二:FindAsync 逐个查询(小数据量 ⭐)

如果 ID 列表很短(比如页面批量操作选 10-20 条),直接用主键查:

var users = new List<sys_admin>();
foreach (var id in ids)
{
    var user = await _db.sys_admin.FindAsync(id);
    if (user != null) users.Add(user);
}
  • ✅ FindAsync 走主键索引直查,不生成 CTE
  • ✅ 简单可靠
  • ❌ N+1 查询,ID 数量多时性能差

适用:后台管理的批量操作(用户勾选几条记录删除/启用等),ID 数量通常不超过几十个。

方案三:全量拉到内存过滤(小表 ⭐)

var all = await _db.sys_admin.ToListAsync();
var users = all.Where(x => ids.Contains(x.id)).ToList();
  • ✅ 零 SQL 风险
  • ✅ 一句话搞定
  • ❌ 全表拉到内存,表大了就是灾难
  • ❌ Contains 在内存中走 LINQ to Objects,没有 SQL 问题

适用:字典表、配置表等行数很少(<1000)的表。

方案四:ToArray()(碰运气)

有时候 List<int> 换成 int[] 后,EF 生成的 SQL 就不同了:

var idArray = ids.ToArray();
var users = await _db.sys_admin
    .Where(x => idArray.Contains(x.id))
    .ToListAsync();
  • ⚠️ 不保证有效,取决于具体的 EF Core 8.x 小版本和查询复杂度
  • ⚠️ 同一套代码在不同环境可能表现不一致
  • ❌ 不推荐作为可靠方案

方案五(EF 9 专属):TranslateParameterizedCollectionsToConstants

如果你已经升级到 EF Core 9,可以用新增的配置项恢复旧行为:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(connectionString)
        .TranslateParameterizedCollectionsToConstants();
}

或者:

builder.Services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(connectionString,
        sqlOptions => sqlOptions.TranslateParameterizedCollectionsToConstants()));
  • ✅ 全局生效,一行配置解决
  • ✅ 恢复 EF Core 7 的 Contains 翻译方式
  • ❌ 回到旧行为:查询计划缓存问题依旧存在(微软当初改它的原因)
  • ❌ 需要 EF Core 9+

5. 推荐策略

你的 SQL Server 版本 >= 2016 且兼容级别 >= 130?
  └─ 是 → 方案一(FromSqlRaw)或方案二(FindAsync),保持 EF8 的新行为
  └─ 否 → 考虑升级 SQL Server 或兼容级别

你的项目还在 .NET 8 / EF Core 8?
  └─ 批量操作(已知 IDs)→ 方案一 或 方案二
  └─ 动态查询(用户输入过滤)→ 直接用 EF8 的 OPENJSON 方式不会触发 CTE 问题
  └─ 小表兜底 → 方案三

你的项目已升级到 EF Core 9?
  └─ 考虑方案五,但要理解它的代价(查询计划缓存退化)

6. 实战案例:MiePcb 项目经验

我们在 MiePcb 管理后台项目中先后踩了两次这个坑:

踩坑 1:GetPageList 的部门查询

// 错误写法
query = query.Where(x => deptIds.Contains(x.dept_id));

报错:WITH 附近语法错误。

修复:内存全量拉取部门表,再 Join 过滤。

var depts = await _db.sys_dept.Select(d => new { d.id, d.name }).ToListAsync();
// 后续在内存中关联

踩坑 2:BatchDelete 的批量删除

// 错误写法
var users = await _db.sys_admin.Where(x => ids.Contains(x.id)).ToListAsync();

报错:同上。

修复:方案一(FromSqlRaw + 参数化),一次查询干净利落。

经验总结

  1. EF Core 8 的项目,所有 Contains 都要在心里打个问号 — 写的时候就要想好万一报错走哪个方案
  2. List<int?> 比 List<int> 更容易触发 — 可空类型让 EF 生成的 SQL 更复杂,更倾向 CTE
  3. SQL Server 错误 156 看到 WITH 就看 Contains — 排查方向比错误信息本身更重要
  4. 记录到 MEMORY.md — 团队其他人可能不知道这个坑,要有文档沉淀

一句话总结:EF Core 8 把 Contains 翻译从"内联常量"改成了"CTE / OPENJSON",CTE 要求前面有分号但 EF 没补,SQL Server 就炸了。修起来简单——用 FromSqlRaw 参数化查询、FindAsync、或者内存过滤。核心原则:不再盲目信任 EF 的 Contains 翻译,批量操作优先 Raw SQL。

我是刚子,一个还在写 .NET 代码的程序员。如果觉得文章内容还可以,记得收藏、转发给更多的小伙伴。咱们下回见!

阅读原文:EF Core 8 + SQL Server:Contains() 突然报 "关键字 WITH 附近有语法错误"?一篇避坑指南 - 码农刚子的开发笔记

ef core $ 附近有语法错误_ef core contains $符近语法错 安装 SQL Server 2016 或更高版本的数据库,或者修改数据库本身的兼容性设置到 SQL Server 2016 或更高。而且即便数据库引擎版本达到,数据库本身的兼容性设置如果低于 SQL Server 2016 的话,也会报错。是因为 .net 8 优化了 Contains 子句的 SQL 翻译,之前的 IN 查询有性能问题。但这个优化只有 SQL Server 2016 及以上版本的数据库支持,否则就会报这个错。 阅读详情

相关推荐

EF Core 8 Contains方法引发SQL Server语法错误的排查与解决

在数据库应用开发中,ORM框架的查询翻译机制直接影响SQL生成与执行效率。Entity Framework Core作为.NET平台主流ORM,其LINQ查询通过特定规则转换为SQL语句,其中Contains方法通常对应SQL的IN子句,用于高效执行集合成员检查。然而,在EF Core 8版本中,为优化参数化查询与执行计划缓存,框架会智能引入公共表表达式(CTE)重构查询逻辑,这体现了ORM框架在平衡开发便利性与数据库性能方面的持续演进。这种优化虽能提升复杂查询场景下的性能,却可能因生成的WITH语句与特定

weixin_31220401的博客 284

突然报 “关键字 WITH 附近有语法错误“?一篇避坑指南

大家好,我是码农刚子,最近遇到一个EF Core 查询的问题,跟大家分享下。升级到 .NET 8 / EF Core 8 后,原来跑得好好的——这篇文章帮你搞清楚为什么、怎么修、以后怎么写才不踩坑。

RH5G3LANy的博客 247

EF Core 8 中 $ 符号引发的语法错误:深入解析与解决方案

本文深入解析了EF Core 8中因$符号引发的语法错误问题,详细介绍了错误原因及多种解决方案。EF Core 8为提高查询性能改用STRING_SPLIT函数,但需要SQL Server 2016及以上版本支持。文章提供了数据库升级、兼容性调整及代码优化等实用解决方案,帮助开发者高效应对这一常见问题。

weixin_29196613的博客 392

EF Core 8 中 $ 符号引发的语法错误:深入解析与兼容性解决方案

本文深入解析了在升级至EF Core 8后,使用Contains查询时因生成带`$`符号的SQL语法而引发的错误。该问题源于EF Core 8为提升性能,将列表查询优化为使用SQL Server 2016+的OPENJSON函数,若数据库版本或兼容性级别过低则报错。文章提供了升级数据库或通过代码配置回退至旧语法两种核心解决方案。

ff678的博客 465

SQL Server 兼容性设置:EF Core 8 查询优化的隐藏坑

本文深度解析了EF Core 8将Contains查询从IN子句优化为OPENJSON策略时,因SQL Server数据库兼容性级别过低而引发'$'符号语法错误的根本原因。文章指出,该优化要求数据库兼容性级别至少为130(SQL Server 2016+),并提供了检查级别、提升级别或配置代码级兼容性等实战解决方案,帮助开发者规避升级陷阱,确保查询性能与稳定性。

weixin_29313423的博客 403

Screenshot_2026-11-03-16-23-09-34.jpg

Screenshot_2026-11-03-16-23-09-34.jpg

EF Core 8升级后Contains查询报错:WITH语法错误排查与解决

在数据库查询优化中,IN运算符和参数化查询是处理集合筛选的常见技术。其原理是通过预编译参数列表来构建SQL查询,以提高安全性和执行计划重用率。这项技术的核心价值在于平衡查询性能与安全性,尤其在处理动态数据筛选时至关重要。然而,当ORM框架引入新的查询翻译策略时,可能会与特定数据库环境产生兼容性问题。例如,EF Core 8为提升Contains()方法的执行效率,会采用公共表表达式(CTE)优化查询结构,但这在某些低版本SQL Server或低兼容性级别数据库中会触发语法错误。本文针对这一典型场景,深入剖析

weixin_33860553的博客 379

研发流程后端开发工具,提示功能开发效率

后端开发工具

SQL Server 兼容性设置导致 EF Core Contains 查询失败?手把手教你修复

本文深入解析SQL Server兼容性设置导致EF Core Contains查询失败的常见问题,提供详细的诊断步骤和解决方案。通过修改兼容性级别或使用临时SQL查询,开发者可以快速修复语法错误,确保EF Core 8的查询优化功能正常工作。文章还涵盖最佳实践、性能对比和高级应用场景,帮助开发者全面应对数据库兼容性挑战。

weixin_30776273的博客 466

机器人具有扰动抑制功能的有限时间基于方位角的领航者-跟随者编队控制(Matlab代码实现)

内容概要:本文档标题为《Plecs全桥LLC谐振变换器仿真模型.txt》,但实际内容并未涉及该主题的具体技术细节或仿真建模过程。相反,文档实质是一份由科研服务团队发布的跨学科资源目录,集中展示了大量基于Matlab、Simulink、Python、PSCAD等平台实现的科研项目与算法开发案例,涵盖电力系统、无人机路径规划、微电网优化、状态估计、信号处理、车间调度、图像处理、通信技术、雷达追踪、智能优化算法等多个工程技术领域。每个条目以“【】”标注研究主题,并注明“Matlab代码实现”“Python代码实现”等形式,表明其为可提供技术支持与代码资源的服务项目。文档末尾频繁出现“完整资源下载:公众号——荔枝科研社”及百度网盘链接,明确体现出其作为科研辅助服务推广与引流工具的功能属性。; 适合人群:面向电气工程、自动化、控制科学与工程、计算机科学、智能制造、新能源、航空航天、通信工程及相关交叉学科的研究生、高校教师、科研人员及企业研发工程师。; 使用场景及目标:①为科研人员提供课题选题、技术路线设计与算法实现的参考范例;②辅助学生或研究人员完成课程设计、毕业论文、项目仿真与算法复现任务;③作为第三方技术服务团队的能力展示目录,便于潜在客户快速了解其在多领域提供的定制化仿真与算法开发服务范围。; 阅读建议:此文档并非传统意义上的学术论文或技术教程,而是一份高度碎片化、广告性质浓厚的资源索引清单。读者应忽略其误导性的文件名,将其视为一个综合性科研技术服务目录,重点关注自身研究方向相关的条目,并通过文末提供的公众号或网盘链接进一步获取具体代码与资料。同时,需注意甄别资源的技术质量、原创性与适用性,避免盲目依赖外部服务。

科技中介服务机构如何打造高效、可复制的技术交易服务流程?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

EF Core 8升级后Contains查询报错:WITH语法错误分析与解决方案

在数据库查询优化中,参数化查询是防止SQL注入和提升性能的重要技术。其原理是将查询逻辑与数据分离,通过参数占位符传递值。EF Core作为.NET主流ORM框架,在版本8中对SQL Server的Contains查询生成策略进行了重大优化,旨在处理长列表时提升性能。这项优化将IN子句转换为使用VALUES的CTE(公共表表达式)实现,但在处理单元素列表等边界情况时,可能生成错误SQL导致“WITH附近语法错误”。对于依赖EF Core进行数据访问的开发者,理解这一底层机制变化至关重要,特别是在从早期版本升级

weixin_30984001的博客 226

高校技术转移办公室人员如何提升科研成果的市场推广与转化成功率?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

EFcore8和Sql Server 2014冲突

摘要:EFCore 8升级后出现Contains方法报错,提示"WITH关键字语法错误"。原因是EFCore版本过高而SQL Server版本过低。解决方案:1)升级SQL Server;2)在DbContext配置中添加UseCompatibilityLevel(120)启用兼容模式,关键代码为o => o.UseCompatibilityLevel(120),可使EFCore8兼容低版本SQL Server。

woflyoycm的博客 499

1.2 重叠因子:双重指数移动平均(Double Exponential Moving Average, DEMA)概念与Python实战

双重指数移动平均(Double Exponential Moving Average, DEMA)是一种改进的移动平均线指标,由Patrick Mulloy在1994年开发。相比传统的指数移动平均线(EMA),DEMA通过减少滞后性来提供更快的市场反应。它通过两次计算EMA并进行特殊组合来实现这一目标,可以在保持平滑性的同时减少滞后。

Kewin的博客 2169

Linux8.3上装Oracle 19c RAC,ssh互信总报INS-06006?别急着下补丁,试试这个临时替换scp的骚操作

本文针对在Linux 8.3上安装Oracle 19c RAC时遇到的SSH互信报错INS-06006问题,提供了一个无需官方补丁的临时解决方案。通过创建SCP包装脚本并添加-T参数,有效解决了OpenSSH 8.x版本与Oracle安装程序的兼容性问题,帮助用户顺利完成安装流程。

weixin_30764883的博客 403

假设你有这样一段再普通不过的代码:

如果当前 SQL 批处理中 EF 没有在前面补上分号,SQL Server 就会报错 156。EF Core 8 不再内联常量,而是通过 OPENJSON 或 CTE(公用表表达式) 来传递参数化集合。EF Core 8 的项目,所有 Contains 都要在心里打个问号 — 写的时候就要想好万一报错走哪个方案。└─ 是 → 方案一(FromSqlRaw)或方案二(FindAsync),保持 EF8 的新行为。stringList.Contains(x.name) 🟡 中(可能走 OPENJSON)

cOg4o7KMN的博客 183

Linux初级介绍之番外篇:Linux挂载windows中的共享目录

挂载windows共享目录Windows端共享权限设定解禁Guest设置共享目录权限Linux挂载端扫描共享目录挂载共享目录遇到错误的解决方案 Windows端共享权限设定 解禁Guest 右击我的电脑选择管理–>本地用户和组–>在用户项中双击Guest用户–>取消账号已禁用选项 设置共享目录权限 右击共享目录点属性–>在共享页面中点击共享选项–>下拉选择共享对象为Everyone–>点击共享按钮 3.设置本地策略 打开命令框–>输入secpol.msc–&gt

m0_53497201的博客 228

SQL注入漏洞实战:从原理到修复的Web应用安全审计指南

SQL注入作为Web应用安全领域的核心威胁,其原理在于应用程序将用户输入的数据与SQL查询语句进行不安全的拼接,导致数据被数据库引擎误解为可执行的代码指令。理解这一漏洞机制对于构建安全防线至关重要,其技术价值在于揭示了数据与代码分离的安全编程范式。在工程实践中,无论是企业级项目管理系统还是各类在线服务平台,只要涉及用户输入与数据库交互,就可能面临此类风险。通过参数化查询(预编译语句)可以从根本上杜绝注入,而结合自动化扫描工具与代码审计能有效发现类似`TianBaoJiLu.aspx`页面的历史遗留问题。本文

weixin_34348174的博客 686

【WRF-Urban】使用 CGLC-MODIS-LCZ_100m 数据集运行 WRF 时的城市参数化问题

【WRF-Urban】使用 CGLC-MODIS-LCZ_100m 数据集运行 WRF 时的城市参数化问题

WW、forever的博客 711

[知识图谱构建] 一.Neo4j图数据库安装初识及药材供应图谱实例

2012年5月,谷歌公司的知识图谱(Knowledge Graph)产品被正式提出,其旨在将人、地点、物等信息作为实体,将实体间的联系作为关系,并将实体和关系以图的形式进行存储。作为语义网的最新产物,知识图谱这种新型的搜索引擎改变了传统的文本搜索结果,而随后国内公司也开发出了相应产品,例如百度公司的“知心”和搜狗公司的“知立方”等。知识图谱本质上属于一种语义网。知识图谱中的每一条数据或事实一般会采用 &amp;lt;实体,属性,属性值&amp;gt; &amp;lt;实体, 关系,

杨秀璋的专栏 1万+
上一篇: .NET 8 Web开发入门(七):安全门禁——JWT 身份验证与授权实战
下一篇: 为什么你的 AI 工具没人用?产品做完 ≠ 有人会用
码农刚子.
博客等级 码龄8年 554粉丝 129原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农刚子.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值