2008-11-14 82 views
6

我試圖從MySQL數據庫中獲取數據。F#初學者:從服務器檢索數據數組

方法2 - 申請/地圖風格

我使用MySQL ADO Reference試圖建立這個系統。特別是21.2.3.1.7處的例子。

(使用僞代碼)

let table = build_sequence(query.read) 

凡query.read返回表中的行(或更確切地說,這恰好是在表中的行元素的列表)。而表變量是一個列表,它將表示查詢返回的表。

我盯着下面給出的代碼,恐怕它的語法超出了我的頭。

方法1 - 循環。

問題1:它不雅,需要一個可變的。

問題2:這只是感覺錯誤,根據我以前使用Prolog的經驗& Lisp。有更多的... 功能方式來做到這一點。

我不確定從哪裏開始。評論&想法?

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

回答

9

的序列類型有處理數據庫遊標稱爲generate_using一個整潔的功能(參見F# ManualFoundations of F#數據訪問一章)。這是一個更高階的函數,它使用一個函數打開遊標,另一個函數(重複調用)來處理遊標中的記錄。下面是一個使用generate_using執行一個SQL查詢的一些代碼:

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 

例如,要列出我們需要定義一個列定義類型和調用executeSqlReader如下Oracle數據庫中的所有表:

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

好的。我仍然不完全熟悉所有的語法元素,但我遵循語義,它回答了我的問題。非常感謝你。 – 2008-11-17 16:32:28

2

以非必要的方式處理命令式API非常困難。我沒有MySql的方便,但我做了一個近似,希望這將提供靈感。 Seq.unfold是一種功能,讓人們一旦發現它,它就會發現非常棒的功能。 List.init(或Array.init)也可以在不使用可變參數的情況下初始化已知大小的數據結構。

#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