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

20
задан Martijn Pieters 3 June 2013 в 06:04
поделиться

3 ответа

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

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

32
ответ дан 29 November 2019 в 23:40
поделиться

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

Я написал функциональную оболочку над 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)
7
ответ дан 29 November 2019 в 23:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: