f# - 错误:阅读器关闭时调用 Read 的尝试无效?
问题描述
定义了以下类型。
type DownloadedItem = { Period: DateTime; Name: string } with
static member fromRdr(rdr:IDataReader) =
{ Period = rdr.GetDateTime 0; Name = rdr.GetString 1 }
static member asSeq (rdr:IDataReader) = seq {
while rdr.Read() do yield DownloadedItem.fromRdr rdr }
然后尝试从数据库表中获取数据。
let files =
let sql = "exec [sp_name] @StartPeriod"
use conn = new SqlConnection(Shared.connectionString)
use cmd = new SqlCommand(sql, conn)
cmd.Parameters.Add("@StartPeriod", SqlDbType.Date).Value <- StartPeriod
conn.Open()
use reader = cmd.ExecuteReader()
reader
|> DownloadedItem.asSeq
上面的表达式可以毫无问题地发送到 F# 交互窗口。
但是,评估files;;
得到以下错误?
val it : seq<DownloadedItem> =
Error: Invalid attempt to call Read when reader is closed.
解决方案
序列是惰性的。这意味着在有人尝试获取其元素之前,不会对序列进行评估。
尝试这个:
let s = seq {
for i in 1..1000 do
printfn "%d" i
yield i
}
> Seq.take 3 s
这个程序只打印从 1 到 3 的数字,即使序列的定义是 1000。这是因为Seq.take 3
调用只枚举了序列的前三个元素,并且评估不会进一步进行。
现在让我们再走一步:
let s =
printfn "Creating sequence"
let result = seq { printfn "Returning item"; yield 42 }
printfn "Done creating sequence"
result
执行此代码将打印“创建序列”,然后打印“完成创建序列”。但它根本不打印“Returning item”。为什么不?我们已经构建了序列,但从未评估过它。现在,如果我执行s
,则会打印“Returning item”。
看看发生了什么?在评估结果序列之前s
完成的执行主体。
同样的事情发生在您的代码中:在files
评估结果序列之前完成执行的主体。并且当主体files
完成执行时,将reader
被处置,因为它与use
. 因此,当你开始评估序列时,reader
它不再有效,所以你得到了错误。
要解决此问题,您需要确保reader
在评估序列的整个过程中保持有效。use
唯一可行的方法是在序列主体中包含所有s:
let files = seq {
let sql = "exec [sp_name] @StartPeriod"
use conn = new SqlConnection(Shared.connectionString)
use cmd = new SqlCommand(sql, conn)
cmd.Parameters.Add("@StartPeriod", SqlDbType.Date).Value <- StartPeriod
conn.Open()
use reader = cmd.ExecuteReader()
while rdr.Read() do yield DownloadedItem.fromRdr reader
}
这样,每次有人尝试枚举序列时都会发生整个初始化,并且reader
在枚举完成之前保持有效。
推荐阅读
- javascript - Ajax 的问题:控制台显示“预期:”
- material-ui - 导入自定义 svg 图标并显示它的最佳方式
- r - 如何传播数据框(从长到宽)并保留两个字段的数据?
- rxjs - 仅从 ngrx 存储选择器中获取不同的值
- javascript - Angular 7 - 仅定期从服务器获取 json 并本地存储在 Angular 应用程序中
- jvm - JVM 解释器(不是 JIT 编译器)实际上是做什么的?
- node.js - 如何将用Java编写的Des-ede算法加密转换为NodeJS
- calendar - Ionic 4 App 中的全年日历显示
- java - 在catch块中抛出异常没有用吗?
- java - @RunWith(Suite.class) @Suite.SuiteClasses({ 数组 })