programming net languages language .net ado.net f# functional-programming

languages - .net language



F#y ADO.NET-F#idiomático (3)

Bueno, no es mucho lo que puede cambiar en el primer bit, pero cuando esté procesando colecciones de datos como en las últimas filas, puede usar las funciones integradas 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"])

Estoy empezando a aprender F #. Escribí este código F # / ADO.NET anoche. ¿De qué manera mejorarías la sintaxis: hacerla sentir como F # idiomática?

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"]

Nota: Encontré que al verificador de sintaxis no le gustaba rowCol. [I]. ["Apellido"] ¿Cuál es la forma correcta de manejar los indizadores dobles? Tuve que dividir el código en dos líneas.

Además, si no hubiera seguido la ruta del DataSet y hubiera usado un SqlDataReader que cargaba sus datos en los registros F #. ¿Qué estructura de colección debo usar para contener los registros? ¿La lista estándar de .NET <>?


Escribí una envoltura funcional sobre ADO.NET para F # . Con esta biblioteca su ejemplo se ve así:

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)


La parte clave de su código tiene que ver con la API .NET que no es funcional, por lo que no hay forma de hacer que esta parte del código sea más idiomática o agradable. Sin embargo, la clave en la programación funcional es la abstracción , por lo que puede ocultar este código (feo) en alguna función idiomática y reutilizable.

Para representar colecciones de datos en F #, puede usar el tipo de lista F # estándar (que es bueno para el procesamiento de datos funcionales) o seq<''a> (que es estándar .NET IEnumerable<''a> debajo de la cubierta), que funciona muy bien cuando se trabaja con otras bibliotecas .NET.

Dependiendo de cómo acceda a la base de datos en otra parte de su código, lo siguiente podría funcionar:

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

Ahora puede usar la función de query para escribir su código original aproximadamente de la siguiente manera:

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

Otro ejemplo que podrías escribir es:

// 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: Respecto a la sugerencia de Mau , no usaría excesivamente las funciones de orden superior si hubiera una forma más directa de escribir el código usando construcciones de lenguaje como for ejemplo. El ejemplo con iter anterior es bastante simple y algunas personas lo encontrarán más legible, pero no hay una regla general ...