F # и ADO.NET - идиоматический F # - PullRequest
19 голосов
/ 21 июня 2010

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

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

Ответы [ 3 ]

31 голосов
/ 21 июня 2010

Ключевая часть вашего кода имеет дело с .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 выше достаточно прост, и некоторые люди сочтут его более читабельным, но нет общего правила ...

7 голосов
/ 21 августа 2010

Я написал функциональную оболочку для 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)
4 голосов
/ 21 июня 2010

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