2013-03-06 3 views
0

Я новичок в F # и пытаюсь погрузиться в первую очередь и сделаю более официальное введение позже. У меня есть следующий код:Как использовать индексирование строк с помощью IDataReader в F #?

type Person = 
    { 
     Id: int 
     Name: string 
    } 

let GetPeople() = 
    //seq { 

    use conn = new SQLiteConnection(connectionString) 
    use cmd = new SQLiteCommand(sql, conn) 

    cmd.CommandType <- CommandType.Text 

    conn.Open() 
    use reader = cmd.ExecuteReader() 
    let mutable x = {Id = 1; Name = "Mary"; } 

    while reader.Read() do 
     let y = 0 
     // breakpoint here 
     x <- { 
     Id = unbox<int>(reader.["id"]) 
     Name = unbox<string>(reader.["name"]) 
     } 
    x 

    //} 

let y = GetPeople() 

Я планирую заменить тело цикла на инструкцию yield и очистить код. Но сейчас я просто пытаюсь убедиться, что доступ к данным работает, отлаживая код и глядя на datareader. В настоящее время я получаю System.InvalidCastException. Когда я помещаю точку останова в точку, указанную в указанной строке выше, а затем введите непосредственные окна reader["name"] Я получаю действительное значение из базы данных, поэтому я знаю, что он подключается к db в порядке. Однако, если я попытаюсь поставить reader["name"] (в отличие от reader.["name"]) в исходном файле, я получаю сообщение «Это значение не является функцией и не может быть применено».

Почему я могу использовать reader["name"] в непосредственном окне, но не в моем fsharp-коде? Как я могу использовать string indexing с читателем?

Update

Следуя совету Джека П., я отщепляюсь код на отдельные строки и теперь я вижу, где происходит ошибка:

let id = reader.["id"] 
    let id_unboxed = unbox id // <--- error on this line 

id имеет тип object {long} в соответствии с отладчик.

ответ

3

В ближайшем окне вы можете использовать reader["name"], потому что в непосредственном окне используется синтаксис C#, а не синтаксис F #.

Замечание: поскольку F # гораздо более кратким, чем C#, в одной строке может быть много чего. Другими словами, установка точки останова на линии может не помочь вам сузить проблему. В таких случаях я обычно «расширяю» выражение на несколько let-bindings на нескольких строках; это облегчает переход через выражение и поиск причины проблемы (в этот момент вы можете просто внести изменения в исходный однострочный шрифт).

Что произойдет, если вы выберете элемент доступа, а unbox вызовет в свои собственные привязки? Например:

while reader.Read() do 
    let y = 0 
    // breakpoint here 
    let id = reader.["id"] 
    let id_unboxed : int = unbox id 
    let name = reader.["name"] 
    let name_unboxed : string = unbox name 
    x <- { Id = id_unboxed; Name = name_unboxed; } 
x 
+0

Это было полезно. Ошибка 'let id_unboxed = unbox id' имеет ошибку. Глядя на тип 'id' в отладчике, он показывает' object {long} '. В ближайшем окне 'id is long 'возвращает true, а' id is int' возвращает false. Поэтому я предполагаю, что это имеет какое-то отношение к int vs long casting. Будем экспериментировать дальше. – User

+1

@User Если это «длинный», вам нужно распаковать его как 'int64' (синтаксис F # для' long'). Я думаю, вам нужно будет распаковать его на правильный тип ('int64') *, а затем приложить его к' int', если это то, что вам нужно - вы не можете просто удалить его непосредственно (это вызывает ошибку, сейчас смотрю). –

+0

Если я изменяю свой тип Person, чтобы использовать 'int64' вместо' int', он работает. Не уверен, почему он тянет идентификатор так долго от db. – User

4

Джек уже ответил на вопрос относительно другой синтаксис для индексации в F # и в ближайшем окне или часов, так что я пропущу это.

По моему опыту, наиболее распространенной причиной для получения System.InvalidCastException при чтении данных из базы данных является то, что значение, возвращаемое reader.["xyz"] фактически DbNull.Value вместо фактической строки или целого числа. Кастинг DbNull.Value в целом или строка потерпит неудачу (потому что это специальное значение), так что если вы работаете с обнуляемыми столбцами, вы должны проверить это явно:

let name = reader.["name"] 
let name_unboxed : string = 
    if name = DbNull.Value then null else unbox name 

Вы можете сделать код лучше, определив ?, который позволяет вам написать reader?name для выполнения поиска.Если вы имеете дело с нулями вы можете также использовать reader?name defaultValue со следующим определением:

let (?) (reader:IDataReader) (name:string) (def:'R) : 'R = 
    let v = reader.[name] 
    if Object.Equals(v, DBNull.Value) then def 
    else unbox v 

код становится:

let name = reader?name null 
let id = reader?id -1 

Это должно также упростить отладку, как вы можете шаг в реализацию ? и посмотреть, что происходит.