0

私は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ステートメントに置き換えて、コードをクリーンアップする予定です。しかし今は、コードをデバッグしてデータリーダーを確認することで、データアクセスが機能することを確認しようとしています。現在、私はを取得していSystem.InvalidCastExceptionます。上記のコメント行で示されたポイントにブレークポイントを設定し、すぐにウィンドウreader["name"]に入力すると、データベースから有効な値が取得されるため、データベースに接続していることがわかります。ただし、reader["name"](ではなくreader.["name"])ソースファイルに入れようとすると、「この値は関数ではなく、適用できません」というメッセージが表示されます。

即時ウィンドウで使用できるのにreader["name"]、fsharpコードでは使用できないのはなぜですか?リーダーで文字列インデックスを使用するにはどうすればよいですか?

アップデート

Jack P.のアドバイスに従って、コードを別々の行に分割すると、エラーが発生した場所がわかります。

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

idobject {long}デバッガに応じたタイプです。

4

2 に答える 2

4

ジャックは、F#とイミディエイトウィンドウまたはウォッチでのインデックス作成のさまざまな構文に関する質問にすでに回答しているので、スキップします。

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?namenullを処理している場合は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

?これにより、の実装にステップインして何が起こっているかを確認できるため、デバッグも簡素化されます。

于 2013-03-06T03:37:51.367 に答える
3

reader["name"]イミディエイト ウィンドウは F# 構文ではなく C# 構文を使用するため、イミディエイト ウィンドウで使用できます。

注意すべき点は、F# は C# よりもはるかに簡潔であるため、1 行で多くのことが行われる可能性があることです。つまり、行にブレークポイントを設定しても、問題を絞り込むのに役立たない場合があります。そのような場合、私は通常、式を複数行の複数の 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
于 2013-03-06T03:26:15.777 に答える