如何在F#中使用IDataReader进行字符串索引?

时间:2013-03-06 03:10:40

标签: database sqlite f# data-access 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 ok。但是,如果我尝试在源文件中放置reader["name"](而不是reader.["name"]),我会得到“此值不是函数且无法应用”消息。

为什么我可以在即时窗口中使用reader["name"]但不能在我的fsharp代码中使用?{1}}?如何将string indexing与读者一起使用?

更新

按照Jack P.的建议,我将代码分成不同的行,现在我看到错误发生的地方:

    let id = reader.["id"]
    let id_unboxed = unbox id // <--- error on this line
根据调试程序,

id的类型为object {long}

2 个答案:

答案 0 :(得分: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

这也应该简化调试,因为您可以进入?的实现并看看发生了什么。

答案 1 :(得分:3)

您可以在即时窗口中使用reader["name"],因为即时窗口使用C#语法,而不是F#语法。

有一点需要注意:由于F#比C#简洁得多,因此可以在一行内进行很多操作。换句话说,在线上设置断点可能无法帮助您缩小问题范围。在这些情况下,我通常会将表达式“扩展”为多行上的多个let-bindings;这样做可以更容易地逐步执行表达式并找到问题的原因(此时,您可以只更改原始单行)。

如果将项目访问和unbox调用拉入他们自己的let-bindings会发生什么?例如:

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