Подтвердить что ты не робот

F # и ADO.NET - идиоматический F #

Я только начинаю изучать 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. [я]. [ "LastName" ] Каков правильный способ обращения с двойными индексаторами? Мне пришлось разбить код на две строки.

Кроме того, если я не пошел по пути DataSet и использовал SqlDataReader, который загружал свои данные в записи F #. Какую структуру сбора следует использовать для хранения записей? Стандартный список .NET < > ?

4b9b3361

Ответ 1

Ключевая часть вашего кода связана с .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 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")

BTW: Что касается предложения Мау, я бы не использовал чрезмерные функции более высокого порядка, если существует более прямой способ написать код с использованием языковых конструкций, таких как for. Пример с iter выше достаточно прост, и некоторые люди найдут его более читабельным, но нет общего правила...

Ответ 2

Я написал функциональную оболочку над ADO.NET для F #. В этой библиотеке ваш пример выглядит следующим образом:

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)

Ответ 3

Ну, вы не можете изменить в первом бите, но всякий раз, когда вы обрабатываете коллекции данных, как в последних нескольких строках, вы можете использовать встроенные функции 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"])