F# 和 ADO.NET - 惯用的 F#

发布于 2024-09-06 14:31:29 字数 692 浏览 2 评论 0原文

我刚刚开始学习 F#。我昨晚写了这段 F#/ADO.NET 代码。 您将通过哪些方式改进语法 - 使其感觉像惯用的 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"]

注意:我确实发现语法检查器不喜欢 rowCol.[i].["姓氏"] 处理双索引器的正确方法是什么?我必须将代码分成两行。

另外如果我没有沿着 DataSet 路线并使用 SqlDataReader 将其数据加载到 F# 记录中。我应该使用什么集合结构来包含记录? 标准 .NET 列表<>?

I'm just starting to learn F#. I wrote this F#/ADO.NET code last night.
In what ways would you improve the syntax - make it feel like idiomatic 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? 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. What collection structure should I use for containing the records?
The standard .NET List<>?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

伤痕我心 2024-09-13 14:31:29

代码的关键部分涉及不起作用的 .NET API,因此无法使这部分代码更加惯用或更好。然而,函数式编程的关键是抽象,因此您可以将这些(丑陋的)代码隐藏到一些惯用且可重用的函数中。

为了在 F# 中表示数据集合,您可以使用标准 F# 列表类型(这有利于功能数据处理)或 seq<'a>(这是标准 .NET IEnumerable<') a> 在封面下),在与其他 .NET 库一起使用时效果很好。

根据您在代码中其他地方访问数据库的方式,以下内容可能有效:

// 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]) }

现在您可以使用 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")

您可以编写的另一个示例是:

// 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")

顺便说一句:关于Mau 的建议 如果有更直接的方法使用 for 等语言结构编写代码,我不会过度使用高阶函数。上面的 iter 示例很简单,有些人会发现它更具可读性,但没有通用规则......

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. 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.

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:

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. The example with iter above is simple enough and some people will find it more readable, but there is no general rule...

迎风吟唱 2024-09-13 14:31:29

我为 F# 编写了一个 ADO.NET 功能包装。使用这个库,您的示例如下所示:

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)

I wrote a functional wrapper over ADO.NET for 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)
帅气称霸 2024-09-13 14:31:29

嗯,一开始你没有什么可以改变的,但是每当你处理数据集合(比如最后几行)时,你都可以使用内置的 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"])

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.

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"])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文