【问题标题】:F# Beginner: retrieving an array of data from a serverF# 初学者:从服务器检索数据数组
【发布时间】:2008-11-14 21:11:45
【问题描述】:

我正在尝试从 MySQL 数据库中获取数据。

方法 2 - 应用/映射样式

我正在使用MySQL ADO Reference 来尝试构建这个系统。特别是在 21.2.3.1.7 找到的示例。

(使用伪代码)

let table = build_sequence(query.read)

其中 query.read 返回表中的一行(或者更确切地说,恰好是表中一行的元素列表)。 table 变量是一个列表列表,表示从查询返回的表。

我盯着下面给出的代码,恐怕它的语法超出了我的想象。

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

【问题讨论】:

    标签: mysql database f#


    【解决方案1】:

    Seq 类型有一个用于处理数据库游标的简洁函数,称为 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
    

    例如,要列出 Oracle 数据库中的所有表,我们需要定义列定义类型并调用 executeSqlReader,如下所示:

    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"
    

    【讨论】:

    • 好的。我仍然对其中的所有句法元素并不完全满意,但我遵循语义,它回答了我的问题。非常感谢。
    • 我会使用 use 关键字来处理连接,而不是手动进行。
    • 时间过去了,我还是无法让语法自行排序。
    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 2017-03-26
      • 1970-01-01
      • 1970-01-01
      • 2019-12-05
      • 1970-01-01
      • 2016-02-12
      • 1970-01-01
      • 1970-01-01
      • 2019-08-31
      相关资源
      最近更新 更多