2008-11-14 12 views
6

Ich versuche, Daten aus einer MySQL-Datenbank zu greifen.F # Anfänger: Abrufen einer Reihe von Daten von einem Server

Ansatz 2 - apply/Map-Stil

ich die MySQL ADO Reference zu versuchen, dieses System zu bauen. Insbesondere das Beispiel in 21.2.3.1.7.

(ein Pseudo-Code)

let table = build_sequence(query.read) 

Wo query.read eine Zeile in der Tabelle zurückgibt (oder besser gesagt, eine Liste von Elementen, die eine Zeile in der Tabelle passieren sein). Und die Tabellenvariable ist eine Liste von Listen, die eine von der Abfrage zurückgegebene Tabelle darstellen.

Ich habe den unten angegebenen Code angesehen, und es ist die Syntax ist über meinem Kopf, fürchte ich.

Ansatz 1 - Schleifen.

Problem 1: Es ist unelegant, erfordert eine veränderbare.

Problem 2: Dies ist nur fühlt sich falsch, basierend auf meiner Erfahrung mit Prolog & Lisp. Es muss ein mehr sein ... funktional Weg, dies zu tun.

Ich bin mir nicht sicher, wo ich anfangen soll. Kommentare & Gedanken?

let reader : MySql.Data.MySqlClient.MySqlDataReader = command.ExecuteReader() 

let arr = [] 

let mutable rowIter = 0 
let readingLoop() = 
    while(reader.Read()) do 
     rowIter = rowIter + 1 
     for i = 0 to reader.FieldCount do 

      //set arr[someiterator, i] = reader.GetValue[i].ToString()) 

Antwort

9

Der Seq-Typ hat eine ordentliche Funktion für den Umgang mit Datenbank-Cursor generate_using (siehe F# Manual und das Data Access Kapitel in Foundations of F#) genannt. Dies ist eine Funktion höherer Ordnung, die eine Funktion zum Öffnen des Cursors und eine weitere (wiederholt aufgerufene) zum Verarbeiten von Datensätzen vom Cursor verwendet. Hier ist ein Code, der generate_using eine SQL-Abfrage auszuführen verwendet:

let openConnection (connectionName : string) = 
    let connectionSetting = ConfigurationManager.ConnectionStrings.Item(connectionName) 
    let connectionString = connectionSetting.ConnectionString 
    let connection = new OracleConnection(connectionString) 
    connection.Open() 
    connection 

let generator<'a> (reader : IDataReader) = 
    if reader.Read() then 
     let t = typeof<'a> 
     let props = t.GetProperties() 
     let types = props 
        |> Seq.map (fun x -> x.PropertyType) 
        |> Seq.to_array 
     let cstr = t.GetConstructor(types) 
     let values = Array.create reader.FieldCount (new obj()) 
     reader.GetValues(values) |> ignore 
     let values = values 
        |> Array.map (fun x -> match x with | :? DBNull -> null | _ -> x) 
     Some (cstr.Invoke(values) :?> 'a) 
    else 
     None 

let executeSqlReader<'a> (connectionName : string) (sql : string) : 'a list =   
    let connection = openConnection connectionName 

    let opener() = 
     let command = connection.CreateCommand(CommandText = sql, CommandType = CommandType.Text) 
     command.ExecuteReader() 

    let result = Seq.to_list(Seq.generate_using opener generator)   

    connection.Close() 
    connection.Dispose() 
    result 

Zum Beispiel, um eine Liste aller Tabellen in einer Oracle-Datenbank benötigen wir eine Spaltendefinition Typ zu definieren, und rufen Sie executeSqlReader wie folgt:

type ColumnDefinition = { 
    TableName : string; 
    ColumnName : string; 
    DataType : string; 
    DataLength : decimal;     
} 

let tableList = executeSqlReader<ColumnDefinition> 
    "MyDatabase" 
    "SELECT t.table_name, column_name, data_type, data_length FROM USER_TABLES t, USER_TAB_COLUMNS c where t.TABLE_NAME = c.table_name order by t.table_name, c.COLUMN_NAME" 
+0

OK. Ich bin immer noch nicht ganz mit allen syntaktischen Elementen vertraut, aber ich folge der Semantik und sie beantwortet meine Frage. Vielen Dank. –

+0

Ich würde das Schlüsselwort * use * verwenden, um die Verbindung zu entfernen, anstatt sie manuell auszuführen. –

+0

Nachdem die Zeit verstrichen ist, kann ich immer noch nicht die Syntax bekommen, um sich zu sortieren. –

2

Es kann schwierig sein, mit imperativen APIs auf unumgängliche Weise zu arbeiten. Ich habe keine MySql zur Hand, aber ich habe eine Schätzung gemacht, hoffentlich wird dies Inspiration liefern. Seq.unfold ist eine Funktion, die Leute ziemlich toll finden, wenn sie sie erst einmal gefunden haben. List.init (oder Array.init) ist auch praktisch, um Datenstrukturen bekannter Größe ohne Verwendung von Mutables zu initialisieren.

#light 

type ThingLikeSqlReader() = 
    let mutable rowNum = 0 
    member this.Read() = 
     if rowNum > 3 then 
      false 
     else 
      rowNum <- rowNum + 1 
      true 
    member this.FieldCount = 5 
    member this.GetValue(i) = i + 1 

let reader = new ThingLikeSqlReader()  
let data = reader |> Seq.unfold (fun (reader : ThingLikeSqlReader) -> 
    if reader.Read() then 
     Some (List.init reader.FieldCount (fun i -> reader.GetValue(i)), reader) 
    else 
     None) |> Seq.to_list 
printfn "%A" data