雷达智富

首页 > 内容 > 程序笔记 > 正文

程序笔记

EF Core 8 (EF8) Contains报错:Microsoft.Data.SqlClient.SqlException (0x80131904): 关键字 'WITH' 附近有语法错误。

2024-07-19 113

最近将原来.NET6的项目升级到.NET8,用的EF Core版本也相应升级到EF8版本,在查询数据的时候使用Contains运算符的地方报错了。

Microsoft.Data.SqlClient.SqlException (0x80131904): 关键字 'WITH' 附近有语法错误。

关键字 'with' 附近有语法错误。如果此语句是公用表表达式、xmlnamespaces 子句或者更改跟踪上下文子句,那么前一个语句必须以分号结尾。

   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__211_0(Task`1 result)

   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()

   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

引发的异常:“Microsoft.Data.SqlClient.SqlException”(位于 System.Private.CoreLib.dll 中)

Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request.

代码是这样的:

var tagList = tags.Split(',');
var currTags = await _context.Tags.Where(x => tagList.Contains(x.Text)).ToListAsync();

原来运行都没问题,使用.NET8之后就报错了。看了一下输出的SQL语句是这样的:

SELECT [t].[Id], [t].[CreatedAt], [t].[LastModifiedAt], [t].[Text]
FROM [Tags] AS [t]
WHERE [t].[Text] IN (
    SELECT [t0].[value]
    FROM OPENJSON(@__tagList_0) WITH ([value] nvarchar(50) '$') AS [t0]
)

我使用的数据库是SQL Server2014,怀疑是.NET8版本不支持了。在微软官方文档找到了原因。原文:https://learn.microsoft.com/zh-cn/ef/core/what-is-new/ef-core-8.0/breaking-changes#contains-in-linq-queries-may-stop-working-on-older-sql-server-versions

影响较大的更改,LINQ 查询中的 Contains 可能会在较旧的 SQL Server 版本上停止工作。

因为 .net 8 优化了 Contains 子句的 SQL 翻译,之前的 IN 查询有性能问题,但这个优化只有 SQL Server 2016 及以上版本的数据库支持,否则就会报这个错。而且即便数据库引擎版本达到,数据库本身的兼容性设置如果低于 SQL Server 2016 的话,也会报错。

解决方法:

1. 使用SQL Server 2016 (13.x) 或更高版本。如果数据库是 SQL Server 2016 (13.x) 或更高版本,或者使用的是 Azure SQL,请通过以下命令检查已配置的数据库兼容性级别:

SELECT name, compatibility_level FROM sys.databases;

2. 如果数据库版本确实低于 SQL Server 2016,或者设置为由于某种原因而无法更改的旧兼容级别,请将 EF Core 配置为还原到较旧且效率较低的 SQL,如下所示:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));
更新于:3个月前
赞一波!1

文章评论

评论问答