2010-06-05 17 views
9

Ho un DataReader e voglio tornare raccolta di righe da esso, dopo la lettura di libri per come un giorno io non sono in grado di trovare il modo migliore per farlo in F #. Posso farlo normale modo C# in F #, ma non è questo il motivo per cui io sto usando f #F # lavorare con ciclo while

Ecco quello che sto cercando di realizzare

let values = 
    while reader.Read() do 
     yield reader.GetString(0), reader.GetInt64(1) 

Sopra è come sto cercando di fare

  • tutti i valori vengono raccolti in valori, che potrebbe essere dictinary o tuple o qualsiasi raccolta
  • resa non può essere utilizzato in ciclo while, ma questo è quello che sto cercando di fare

Quale potrebbe essere il modo migliore per raggiungere questo

risposta

13

F # offre anche di lista per array e sequenze.

let records_as_list = 
    [ 
     while reader.Read() 
      do yield (reader.GetString(0), reader.GetInt64(1)) 
    ] 

let records_as_array = 
    [| 
     while reader.Read() 
      do yield (reader.GetString(0), reader.GetInt64(1)) 
    |] 

let records_as_sequence = 
    seq { 
     while reader.Read() 
      do yield (reader.GetString(0), reader.GetInt64(1)) 
    } 

F # ha una comoda funzione di dizionario incorporato in detta dict

let records_as_IDictionary = dict records_as_sequence 
7

È possibile utilizzare espressioni di sequenza per implementare enumeratori:

let records = seq { while reader.NextResult() do yield (reader.GetString(0), reader.GetInt64(1)) } 

Se avete bisogno di più di due campi, è possibile produrre mappe di indice di colonna (o il nome) -> i valori di campo (codice non testato):

let dbSchema = reader.GetSchemaTable() 

let makeMap (rdr : IDataReader) (schema : DataTable) = 
    schema.Columns |> Seq.cast<DataColumn> |> Seq.map (fun col -> (col.ColumnName, rdr.[col.ColumnName])) |> Map.ofSeq 

let records = seq { while reader.NextResult() do yield (makeMap reader dbSchema) } 
+0

problema con seq è che lo fa lazy loading in modo che quando si desidera leggere in questo lettore caso sarebbe chiuso – mamu

+0

facilmente fissato aggiungendo |> Seq.toList per l'ultima riga a materializzare la collezione. – Mau

3

per questo tipo di compito, mi piacerebbe trasferire prima l'ingresso in una sequenza di stringhe.

.Net 4.0 fornisce readlines, il tipo del suo valore di ritorno è seq<string>:

open System.IO 
    let readLinesSeq = File.ReadLines 

Nelle versioni più bassi di .Net, si ha la necessità di implementare questa funzione:

let readLines filePath = seq { 
    use sr = new StreamReader (filePath) 
    while not sr.EndOfStream do 
    yield sr.ReadLine() 
    } 
+0

La domanda riguarda 'System.Data.IDataReader' - non l'inserimento di file ... –

+0

@ Joel..Oops ... L'idea dovrebbe essere simile ... –

+0

Sì, l'idea di base è la stessa. –

0

Bene siamo non parliamo di file qui, tuttavia, nelle precedenti versioni di .NET che puoi fare (con piccoli file):

reader.ReadToEnd().Split(System.Environment.NewLine.ToCharArray()) 

Dove reader è un TextReader.

+0

Se il file è grande, si consiglia di evitare la lettura di tutto il contenuto e la suddivisione. –

+0

In questa domanda, 'reader' è in realtà un IDataReader, non un TextReader –

2

potrei affrontare questo problema più genericamente con l'aggiunta di una proprietà estensione a IDataReader che trasforma ogni datareader in un seq<IDataRecord> ...

[<AutoOpen>] 
module DataReaderEx = 
    open System.Data 

    type IDataReader with 
     member this.toSeq = 
      seq { while this.Read() do yield this :> IDataRecord } 

Ciò consentirebbe di utilizzare le funzioni ordinarie dal Seq modulo su qualsiasi datareader:

reader.toSeq 
|> Seq.map (fun row -> row.GetString(0), row.GetInt64(1))