Ошибка: неверная попытка вызова Read, когда читатель закрыт? - PullRequest
0 голосов
/ 31 мая 2018

Определен следующий тип.

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.

1 Ответ

0 голосов
/ 31 мая 2018

Последовательности ленивы.Это означает, что последовательность не будет оценена, пока кто-то не попытается получить ее элементы.

Попробуйте:

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, будет напечатано «Возвращение предмета».

Видите, что происходит?Тело s завершило выполнение до , и полученная последовательность будет оценена.

То же самое происходит в вашем коде: тело files завершает выполнение до того, как полученная последовательность будет оценена.И когда тело files завершает выполнение, reader удаляется, так как оно было связано с use.Следовательно, к тому моменту, когда вы получите оценку последовательности, 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 сохраняется в силе до завершения перечисления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...