f# - 错误: Invalid attempt to call Read when reader is closed?

标签 f# ado.net

定义了以下类型。

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

执行此代码会打印“创建序列”,然后打印“完成创建序列”。但它根本不打印“返回”。为什么不?我们构建了序列,但从未评估它。现在,如果我执行 s,则会打印“Returning item”。

看看发生了什么? s 的主体在计算结果序列之前完成执行。

您的代码中也会发生同样的情况:files 的主体在计算结果序列之前完成执行。当files 的主体执行完毕后,reader 就会被释放,因为它与use 绑定(bind)在一起。因此,当您开始评估序列时,reader 不再有效,因此您会收到错误。


要解决此问题,您需要确保 reader 在序列评估的整个过程中保持有效。唯一实用的方法是将所有 use 包含在序列主体中:

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 保持有效,直到枚举完成。

关于f# - 错误: Invalid attempt to call Read when reader is closed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50615393/

相关文章:

f# - 在 F# 中使用 Seq.fold 的更好方法

entity-framework - ADO.Net 实体模型 (edmx) 与 Entity Framework (v4.0 等)

c# - 有时在 System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) 处超时 (...)

entity-framework - 您会使用 ADO.NET Entity Framework 的 v3.5,还是等待 v4.0?

f# - 为什么这个函数不是尾递归?

c# - Alea GPU 教程未使用 FSharp.Core 4.4.0.0 在 VS 2015 Update 2 上编译

generics - 是否可以在 ‘T is constrained to be a tuple of type ' T2*'T3 处编写一个 genericFunction<'T>?

f# - 在 Suave 中使用非拉丁字符

unit-testing - TDD 和 ADO.NET Entity Framework

c# - 将数据表转换为数据读取器