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

为何调用FirstOrDefault前需先枚举?SQL语法报错问题解析

问题:SqlQueryRaw调用FirstOrDefault时抛出SQL语法错误,添加ToList()则正常

不清楚为何以下代码在FirstOrDefault前不执行ToList()(枚举操作)时会抛出异常,未找到相关解释,希望有人解答问题所在。

public DateTime? GetOldestUpdDate(int field1Id, DateTime threshold)
{
    var query = @"SELECT MIN(updated_at) AS updated_at FROM my_table WHERE field_1_id = @field_1_id AND updated_at < @threshold;";
  
        return _context.Database.SqlQueryRaw<DateTime?>(
            query,
            new MySqlParameter("field_1_id", field1Id),
            new MySqlParameter("threshold", threshold.DateString()))
            //.ToList() 不添加此方法时,会抛出SQL查询错误
            .FirstOrDefault();
    
}

异常信息

消息:您的SQL语法存在错误;请查看与您的MariaDB服务器版本对应的手册,了解在第3行'; ) AS t LIMIT 2'附近使用的正确语法
堆栈跟踪:
在 MySqlConnector.Core.ResultSet.ReadResultSetHeaderAsync(IOBehavior ioBehavior)(位于 //src/MySqlConnector/Core/ResultSet.cs:第43行)
在 MySqlConnector.MySqlDataReader.ActivateResultSet(CancellationToken cancellationToken)(位于 /
/src/MySqlConnector/MySqlDataReader.cs:第130行)
在 MySqlConnector.MySqlDataReader.CreateAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary2 cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken)(位于 /_/src/MySqlConnector/MySqlDataReader.cs:第468行) 在 MySqlConnector.Core.CommandExecutor.ExecuteReaderAsync(IReadOnlyList1 commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity activity, IOBehavior ioBehavior, CancellationToken cancellationToken)(位于 //src/MySqlConnector/Core/CommandExecutor.cs:第56行)
在 MySqlConnector.MySqlCommand.ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)(位于 /
/src/MySqlConnector/MySqlCommand.cs:第344行)
在 MySqlConnector.MySqlCommand.ExecuteDbDataReader(CommandBehavior behavior)(位于 /_/src/MySqlConnector/MySqlCommand.cs:第278行)
...(后续内容省略)

解答

核心原因是EF Core处理SqlQueryRaw返回的IQueryable<T>时,若后续调用FirstOrDefault()这类LINQ方法,会尝试将原SQL包装成子查询,但你的原SQL结尾带了分号,导致拼接后的SQL语法失效。

从异常信息的错误提示near '; ) AS t LIMIT 2'能看出,EF Core生成的最终SQL是把你的原查询(带分号)套进了(...) AS t LIMIT 2的结构里,分号破坏了子查询的语法结构,因此触发错误。

而调用ToList()时,会直接执行原SQL并将结果加载到内存,不会触发EF Core的LINQ查询转换逻辑,也就不会出现拼接错误。

解决方法

  • 方法一:移除原SQL结尾的分号
    修改SQL字符串,去掉末尾的分号:
    var query = @"SELECT MIN(updated_at) AS updated_at FROM my_table WHERE field_1_id = @field_1_id AND updated_at < @threshold";
    
  • 方法二:先转为内存枚举再取结果
    用AsEnumerable()先将IQueryable<T>转为内存中的枚举集合,再调用FirstOrDefault(),避免EF Core修改原SQL:
    return _context.Database.SqlQueryRaw<DateTime?>(query, new MySqlParameter("field_1_id", field1Id), new MySqlParameter("threshold", threshold.DateString())).AsEnumerable().FirstOrDefault();
    

内容的提问来源于stack exchange,提问作者Andre

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 10:37:06