简体   繁体   English

F#和ADO.NET - 惯用F#

[英]F# and ADO.NET - idiomatic F#

I'm just starting to learn F#. 我刚开始学习F#。 I wrote this F#/ADO.NET code last night. 我昨晚写了这个F#/ ADO.NET代码。 In what ways would you improve the syntax - make it feel like idiomatic F#? 你会以什么方式改进语法 - 让它感觉像是惯用的F#?

    let cn = new OleDbConnection(cnstr)
    let sql = "SELECT * FROM People"
    let da = new OleDbDataAdapter(new OleDbCommand(sql, cn))
    let ds = new DataSet()
    cn.Open()
    let i = da.Fill(ds)
    let rowCol = ds.Tables.[0].Rows
    let rowCount = rowCol.Count
    printfn "%A" rowCount

    for i in 0 .. (rowCount - 1) do
        let row:DataRow = rowCol.[i]
        printfn "%A" row.["LastName"]

Note: I did find the syntax checker did not like rowCol.[i].["LastName"] What is the proper way to handle dual-indexers? 注意:我确实发现语法检查器不喜欢rowCol。[i]。[“LastName”]处理双索引器的正确方法是什么? I had to break up the code over two lines. 我不得不将代码分成两行。

Also If I hadn't gone down the DataSet route and used a SqlDataReader that loaded its data into F# records. 另外,如果我没有沿着DataSet路由走下去并使用将其数据加载到F#记录中的SqlDataReader。 What collection structure should I use for containing the records? 我应该使用什么样的集合结构来包含记录? The standard .NET List<>? 标准的.NET List <>?

The key part of your code deals with .NET API that is not functional, so there is no way to make this part of the code particularly more idiomatic or nicer. 代码的关键部分是处理不起作用的.NET API,因此无法使代码的这一部分更具惯用性或更好。 However, the key thing in functional programming is abstraction , so you can hide this (ugly) code into some idiomatic and reusable function. 但是,函数式编程中的关键是抽象 ,因此您可以将这个(丑陋)代码隐藏到一些惯用和可重用的函数中。

For representing collections of data in F#, you can either use standard F# list type (which is good for functional data processing) or seq<'a> (which is standard .NET IEnumerable<'a> under the cover), which works nicely when working with other .NET libraries. 为了表示F#中的数据集合,您可以使用标准F#列表类型(适用于功能数据处理)或seq<'a> (封面下的标准.NET IEnumerable<'a> ),它可以很好地工作使用其他.NET库时。

Depending on how you access database elsewhere in your code, the following could work: 根据您在代码中的其他位置访问数据库的方式,以下可能有效:

// Runs the specified query 'sql' and formats rows using function 'f'
let query sql f = 
  // Return a sequence of values formatted using function 'f'
  seq { use cn = new OleDbConnection(cnstr) // will be disposed 
        let da = new OleDbDataAdapter(new OleDbCommand(sql, cn)) 
        let ds = new DataSet() 
        cn.Open() 
        let i = da.Fill(ds) 
        // Iterate over rows and format each row
        let rowCol = ds.Tables.[0].Rows 
        for i in 0 .. (rowCount - 1) do 
            yield f (rowCol.[i]) }

Now you can use the query function to write your original code roughly like this: 现在您可以使用query函数来编写原始代码,大致如下:

let names = query "SELECT * FROM People" (fun row -> row.["LastName"])
printfn "count = %d" (Seq.count names)
for name in names do printfn "%A" name

// Using 'Seq.iter' makes the code maybe nicer 
// (but that's a personal preference):
names |> Seq.iter (printfn "%A")

Another example you could write is: 你可以写的另一个例子是:

// Using records to store the data
type Person { LastName : string; FirstName : string }
let ppl = query "SELECT * FROM People" (fun row -> 
  { FirstName = row.["FirstName"]; LastName = row.["LastName"]; })

let johns = ppl |> Seq.filter (fun p -> p.FirstName = "John")

BTW: Regarding the suggestion by Mau I wouldn't use higher-order functions excessively if there is a more direct way to write the code using language constructs such as for . 顺便说一下:关于Mau的建议,如果有更直接的方法使用像for这样的语言结构编写代码,我就不会过度使用高阶函数。 The example with iter above is simple enough and some people will find it more readable, but there is no general rule... 以上iter的例子很简单,有些人会发现它更具可读性,但没有一般规则......

I wrote a functional wrapper over ADO.NET for F# . 在ADO.NET上为F#编写了一个函数包装器 With this library your example looks like this: 使用此库,您的示例如下所示:

let openConn() =
   let cn = new OleDbConnection(cnstr)
   cn.Open()
   cn :> IDbConnection

let query sql = Sql.execReader (Sql.withNewConnection openConn) sql

let people = query "select * from people" |> List.ofDataReader
printfn "%d" people.Length
people |> Seq.iter (fun r -> printfn "%s" (r?LastName).Value)

Well, there's not much you can change in the first bit but whenever you're processing collections of data like in the last few rows, you can use the built-in Seq, List, Array functions. 好吧,你可以在第一位改变,但是无论何时处理数据集合,如最后几行,你都可以使用内置的Seq,List,Array函数。

for i in 0 .. (rowCount - 1) do
  let row:DataRow = rowCol.[i]
  printfn "%A" row.["LastName"]

= =

rowCol |> Seq.cast<DataRow> 
       |> Seq.iter (fun row -> printfn "%A" row.["LastName"])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM