Der Seq-Typ hat eine nette Funktion zur Behandlung von Datenbank-Cursoren namens generate_using (siehe F#-Handbuch und das Kapitel „Datenzugriff“ in Grundlagen von F# ). Dies ist eine Funktion höherer Ordnung, die eine Funktion zum Öffnen des Cursors und eine andere (wiederholt aufgerufen) zum Verarbeiten von Datensätzen vom Cursor benötigt. Hier ist ein Code, der generate_using verwendet, um eine SQL-Abfrage auszuführen:
let openConnection (connectionName : string) =
let connectionSetting = ConfigurationManager.ConnectionStrings.Item(connectionName)
let connectionString = connectionSetting.ConnectionString
let connection = new OracleConnection(connectionString)
connection.Open()
connection
let generator<'a> (reader : IDataReader) =
if reader.Read() then
let t = typeof<'a>
let props = t.GetProperties()
let types = props
|> Seq.map (fun x -> x.PropertyType)
|> Seq.to_array
let cstr = t.GetConstructor(types)
let values = Array.create reader.FieldCount (new obj())
reader.GetValues(values) |> ignore
let values = values
|> Array.map (fun x -> match x with | :? DBNull -> null | _ -> x)
Some (cstr.Invoke(values) :?> 'a)
else
None
let executeSqlReader<'a> (connectionName : string) (sql : string) : 'a list =
let connection = openConnection connectionName
let opener() =
let command = connection.CreateCommand(CommandText = sql, CommandType = CommandType.Text)
command.ExecuteReader()
let result = Seq.to_list(Seq.generate_using opener generator)
connection.Close()
connection.Dispose()
result
Um beispielsweise alle Tabellen in einer Oracle-Datenbank aufzulisten, müssen wir einen Spaltendefinitionstyp definieren und executeSqlReader wie folgt aufrufen:
type ColumnDefinition = {
TableName : string;
ColumnName : string;
DataType : string;
DataLength : decimal;
}
let tableList = executeSqlReader<ColumnDefinition>
"MyDatabase"
"SELECT t.table_name, column_name, data_type, data_length FROM USER_TABLES t, USER_TAB_COLUMNS c where t.TABLE_NAME = c.table_name order by t.table_name, c.COLUMN_NAME"