.NET 8.0升级后特定LINQ语句出现SQL语法错误求助
问题背景
项目从.NET Core 3.1升级至.NET 8.0,同步更新EF Core版本后,执行包含Contains的LINQ语句时触发SQL语法错误,错误提示:
Incorrect syntax near the keyword 'WITH'. Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon.
涉事LINQ语句:
return context.TransactionEvents .Where(x => transList.Contains(x.TransactionId) && x.EventId == 30);
原因分析
EF Core SQL生成逻辑变更:
EF Core 3.x处理Contains时直接生成IN子句;而EF Core 8.x针对元素数量较多的集合(默认阈值为1000),会自动生成带CTE(公共表表达式)的SQL。如果之前的数据库会话中存在未正确终止的SQL语句(缺少分号),就会触发该语法错误。依赖冲突:
项目同时引用了EntityFramework(EF6)和Microsoft.EntityFrameworkCore(EF Core),两个ORM框架的上下文类型可能混淆,导致生成不符合预期的SQL。此外,项目保留的旧版ASP.NET Core MVC(2.x)依赖与.NET 8兼容性不佳,间接影响EF Core的执行逻辑。集合大小触发CTE模式:
当transList的元素数量超过EF Core 8的默认阈值(1000)时,自动启用CTE模式,而旧版数据库驱动或会话状态可能不兼容这种语法。
解决方案
方案1:强制EF Core使用IN子句而非CTE
通过配置修改EF Core的SQL生成行为,强制对Contains使用IN子句:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("你的数据库连接字符串", options => { // 设置最大批量大小为1000,匹配IN子句的SQL限制 options.MaxBatchSize(1000); // 强制使用单查询模式,避免自动切换到CTE options.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery); }); }
如果transList元素数量超过1000,可手动分批查询后合并结果:
var batchSize = 1000; var results = new List<TransactionEvent>(); for (int i = 0; i < transList.Count; i += batchSize) { var currentBatch = transList.Skip(i).Take(batchSize).ToList(); var batchData = context.TransactionEvents .Where(x => currentBatch.Contains(x.TransactionId) && x.EventId == 30) .ToList(); results.AddRange(batchData); } return results;
方案2:清理冲突依赖
移除项目中冗余、冲突的依赖包,确保依赖版本统一:
- 删除
EntityFramework(EF6)的引用,项目使用EF Core,两者不可混用 - 删除旧版
Microsoft.AspNetCore.Mvc.Core和Microsoft.AspNetCore.Mvc.ViewFeatures(2.x版本),改用.NET 8内置的ASP.NET Core组件 - 确保所有EF Core相关包版本统一为8.0.1
修改后的项目文件示例:
<PropertyGroup> <TargetFramework>net8.0</TargetFramework> </PropertyGroup> <ItemGroup> <!-- EF Core核心依赖 --> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.1"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <!-- 如需ASP.NET MVC功能,使用.NET 8对应包 --> <PackageReference Include="Microsoft.AspNetCore.Mvc" Version="8.0.1" /> </ItemGroup>
方案3:验证DbContext继承关系
确保自定义DbContext正确继承自Microsoft.EntityFrameworkCore.DbContext,而非EF6的System.Data.Entity.DbContext,避免类型混淆导致的异常。
验证方法
修改后重新编译项目,开启EF Core日志查看生成的SQL语句,确认是否生成预期的IN子句而非CTE,再执行原LINQ语句验证是否解决错误。
内容的提问来源于stack exchange,提问作者Nikhil_TP

