DEBUG.WIKI
PanstarCloud:部署应用,接入 AI,云服务器与 AI 网关
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →

SqliteDataReader 刚返回就关闭,检查 command 的释放时机

Microsoft.Data.Sqlite 的 command 释放会连带关闭活动 reader。函数里 using command 再返回 reader,调用方可能立即报 reader is closed;让资源所有权覆盖整个读取过程。

函数里创建 SqliteCommand,返回它的 reader;调用方刚执行 Read() 就失败:

Invalid attempt to call Read when reader is closed.

使用 Microsoft.Data.Sqlite 时,要检查函数退出前是否已释放 command。即使连接仍然打开、调用方还没主动关闭 reader,command 的释放也可能提前结束读取。

正式 Microsoft.Data.Sqlite 10.0.13 的 SqliteCommand.Dispose 会清理预备语句,清理过程中会先释放活动的 DataReader。这与某些其他 ADO.NET 驱动的行为不同,不能把另一种驱动上可用的资源生命周期直接照搬过来。

把 command 的存活期延长到读取结束

下面的写法会在函数返回时释放 command,因而把一个已经关闭的 reader 交给调用方:

SqliteDataReader OpenRows(SqliteConnection connection)
{
    using var command = connection.CreateCommand();
    command.CommandText = "SELECT 42";
    return command.ExecuteReader();
}

这里的 using 会在 return 离开作用域时执行,并不会等到返回对象读完。把它改成 await using 也不能解决同样的所有权问题。

如果结果很小、业务本来只需要读取一个值,可以在拥有 command 的作用域内完成读取,再返回普通值。下面是假定连接已打开的函数示例:

long ReadAnswer(SqliteConnection connection)
{
    using var command = connection.CreateCommand();
    command.CommandText = "SELECT 42";
    using var reader = command.ExecuteReader();
    if (!reader.Read())
        throw new InvalidOperationException("Expected one row.");
    return reader.GetInt64(0);
}

示例需要 Microsoft.Data.Sqlite 与 System 命名空间。实际查询应保留参数化、事务和返回类型约定;不能为修复生命周期,把任意大结果集全部装进内存。

流式读取必须明确归谁释放

确实需要让调用方逐行消费时,由调用方在同一个作用域持有 connection、command 和 reader,读取结束后按 reader、command、connection 的顺序释放。若连接由更外层事务管理,仍由原来的事务所有者决定何时关闭它。

数据访问层也可以返回一个同时拥有这些资源的包装对象,但必须明确哪些资源归它管理,正常结束、提前停止和异常时都要释放。单独去掉 using、只返回 reader,又没有人接手 command 的释放,会把“过早关闭”换成资源泄漏。

通过迭代器封装时,资源应保留在真正执行枚举的作用域中,消费者还必须正确结束或释放枚举器。不要在创建枚举器的方法返回前先把底层 reader 关掉。

不要把版本计划当成现有保证

这条问题涉及 command 释放时连带关闭 reader 的具体行为。后续版本即使调整了该行为,连接关闭、显式释放 reader 和重新使用同一 command 仍可能终止读取,不能因此省略所有权设计。

当前若仍使用 10.0.13,应按已核实的生命周期组织代码,不依赖未来版本的处置方式。只有确认正式工件已经包含所需变更并完成兼容性检查,才把升级当作相应行为变化的依据。

复查时分别覆盖读完所有行、只读一行就退出、调用方异常三条路径。修正后 reader 应在业务需要的期间保持打开,结束后所有自有资源都应释放;连接未关闭不再被当作 reader 一定可读的证明。