2010-06-21 2 views
19

Я только начинаю изучать F #. Я написал этот код F #/ADO.NET вчера вечером. Каким образом вы могли бы улучшить синтаксис - заставить его чувствовать себя как идиоматический F #?F # и ADO.NET - 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"] 

Примечание: Я нашел проверки синтаксиса не нравится rowCol [I] [ "LastName"] Что такое правильный способ справиться с двойной-шагового..? Мне пришлось разбить код на две строки.

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

+0

Не уверен, что я понимаю вопрос. – BuddyJoe

+1

Он имел в виду, что задача под рукой (операции с БД с библиотеками .NET) обязательно заканчивается обязательным кодом, поэтому F # не сияет там. Однако это может быть очень удобно для обработки данных, как только вы получите его из БД. – Mau

+0

@tyndall Mau на 100% прав. ваш код на 100% необходим. вы можете преобразовать его в C# с помощью FindReplace. Я думаю, что F # может быть не лучшим инструментом здесь. – Andrey

ответ

30

Ключевая часть вашего кода сделок с .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: Что касается предложения Mau Я бы не использовал чрезмерные функции более высокого порядка, если есть более прямой способ написать код с использованием языковых конструкций, таких как for. Пример с iter выше достаточно прост, и некоторые люди посчитают его более читаемым, но нет общего правила ...

+1

Если вы правильно поняли первый пример, seq {} вызовет повторную базу данных, исправьте? и ваша строка «let ppl =» позволяет избежать проблемы с изменчивостью, установив ее в последовательность. Я на правильном пути здесь? – BuddyJoe

+1

@tyndall: Это хороший момент! Последовательность будет повторно оценена (и запрашивается база данных снова) каждый раз, когда вы используете последовательность (например, используя 'Seq.count' или' for'). Это немного неудачно, и 'let ppl = ..' не избегает этого. Однако вы можете написать 'let ppl = ... |> Seq.cache' или, например,' List.ofSeq' для запуска запроса и получения результата в виде списка. –

+0

Мне нравится идея List.ofSeq. +1 – BuddyJoe

4

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

Я написал functional wrapper over ADO.NET for 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) 
Смежные вопросы