Database 如何在F#中使用带IDataReader的字符串索引?

Database 如何在F#中使用带IDataReader的字符串索引?,database,sqlite,f#,data-access,idatareader,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

我对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()
根据调试器,
id
的类型为
object{long}

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

需要注意的一点是:由于F#比C#简洁得多,因此在一行中可能会发生很多事情。换句话说,在线路上设置断点可能无助于缩小问题范围。在这些情况下,我通常将表达式“扩展”为多行上的多个let绑定;这样做可以更容易地单步遍历表达式并找到问题的原因(此时,您只需更改原始的一行代码即可)

如果将项访问和
unbox
调用拉入它们自己的let绑定,会发生什么情况?例如:

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
当reader.Read()执行以下操作时 设y=0 //断点在这里 let id=读卡器。[“id”] let id_unbox:int=unbox id 让name=读取器。[“name”] let name_unbox:string=unbox name
Jack已经回答了关于F#和immediate window或watchs中索引的不同语法的问题,所以我将跳过这个问题

根据我的经验,从数据库读取数据时获取
System.InvalidCastException
最常见的原因是
reader.[“xyz”]
返回的值实际上是
DbNull.value
而不是实际的字符串或整数。将
DbNull.Value
强制转换为整数或字符串将失败(因为它是一个特殊值),因此,如果使用的是可为null的列,则需要明确检查:

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

这还可以简化调试,因为您可以逐步实现
,并查看发生了什么。

这很有帮助。行
let id_unbox=unbox id
有错误。在调试器中查看
id
的类型,它会显示
对象{long}
。在即时窗口中,
id为long
返回true,而
id为int
返回false。所以我猜这和整型和长型铸造有关。将进一步试验。@User如果它是
长的
,则需要将其作为
int64
(F#语法为
长的
)。我认为您需要将其取消装箱到正确的类型(
int64
),然后将其强制转换为
int
,如果这是您需要的--您不能直接取消装箱(这导致了您现在看到的错误)。如果我将我的个人类型更改为使用
int64
,而不是
int
,则可以正常工作。不确定它为什么要从db中拖出这么长的id。刚刚发现SQLite使用int64作为整数主键,因此可以解释:
let name = reader?name null
let id = reader?id -1