Search code examples
databasesqlitef#data-accessidatareader

How to use string indexing with IDataReader in F#?


I'm new to F# and trying to dive in first and do a more formal introduction later. I have the following code:

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()

I plan to replace the loop body with a yield statement and clean up the code. But right now I'm just trying to make sure the data access works by debugging the code and looking at the datareader. Currently I'm getting a System.InvalidCastException. When I put a breakpoint at the point indicated by the commented line above, and then type in the immediate windows reader["name"] I get a valid value from the database so I know it's connecting to the db ok. However if I try to put reader["name"] (as opposed to reader.["name"]) in the source file I get "This value is not a function and cannot be applied" message.

Why can I use reader["name"] in the immediate window but not in my fsharp code? How can I use string indexing with the reader?

Update

Following Jack P.'s advice I split out the code into separate lines and now I see where the error occurs:

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

id has the type object {long} according to the debugger.


Solution

  • You can use reader["name"] in the immediate window because the immediate window uses C# syntax, not F# syntax.

    One thing to note: since F# is much more concise than C#, there can be a lot going on within a single line. In other words, setting a breakpoint on the line may not help you narrow down the problem. In those cases, I normally "expand" the expression into multiple let-bindings on multiple lines; doing this makes it easier to step through the expression and find the cause of the problem (at which point, you can just make the change to your original one-liner).

    What happens if you pull the item accesses and unbox calls out into their own let-bindings? For example:

    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