2012-06-23 12 views
6

Recentemente ho partecipato a un tutorial di Keith Battochi su provider di tipi, in cui ha introdotto una variante del provider di tipo MiniCsv nell'esercitazione MSDN. Sfortunatamente, il mio laptop non era disponibile, quindi ho dovuto scrivere il codice a mano come meglio potevo. Credo di aver ricreato il prestatore tipo, ma mi stoProvider di tipo F # personalizzato: errore "Tipo di contenitore già impostato"

error FS3033: The type provider 'CsvFileTypeProvider+CsvFileTypeProvider' reported an error: container type for 'CsvFileProvider.Row' was already set to 'CsvFileProvider.CsvFile,Filename="events.csv" 

Quando guardo il codice, non riesco a vedere come sto aggiungendo il tipo di riga al contenitore due volte (o in una certa altro contenitore). La rimozione di righe selezionate del codice non aiuta.

Ecco come sto chiamando il codice FSI:

#r "CsvFileTypeProvider.dll" 
open CsvFileProvider 
let eventInfos = new CsvFile<"events.csv">() ;; 

Ed ecco il codice stesso:

module CsvFileTypeProvider 
open Samples.FSharp.ProvidedTypes 
open Microsoft.FSharp.Core.CompilerServices 

let getType str = 
    if System.DateTime.TryParse(str, ref Unchecked.defaultof<_>) then 
     typeof<System.DateTime>, (fun (str:Quotations.Expr) -> <@@ System.DateTime.Parse(%%str) @@>) 
    elif System.Int32.TryParse(str, ref Unchecked.defaultof<_>) then 
     typeof<System.Int32>, (fun (str:Quotations.Expr) -> <@@ System.Int32.Parse(%%str) @@>) 
    elif System.Double.TryParse(str, ref Unchecked.defaultof<_>) then 
     typeof<System.Double>, (fun (str:Quotations.Expr) -> <@@ System.Double.Parse(%%str) @@>) 
    else 
     typeof<string>, (fun (str:Quotations.Expr) -> <@@ %%str @@>) 

[<TypeProvider>] 
type CsvFileTypeProvider() = 
    inherit TypeProviderForNamespaces() 

    let asm = typeof<CsvFileTypeProvider>.Assembly 
    let ns = "CsvFileProvider" 

    let csvFileProviderType = ProvidedTypeDefinition(asm, ns, "CsvFile", None) 
    let parameters = [ProvidedStaticParameter("Filename", typeof<string>)] 

    do csvFileProviderType.DefineStaticParameters(parameters, fun tyName [| :? string as filename |] -> 
     let rowType = ProvidedTypeDefinition(asm, ns, "Row", Some(typeof<string[]>)) 

     let lines = System.IO.File.ReadLines(filename) |> Seq.map (fun line -> line.Split(',')) 
     let columnNames = lines |> Seq.nth 0 
     let resultTypes = lines |> Seq.nth 1 |> Array.map getType 

     for idx in 0 .. (columnNames.Length - 1) do 
      let col = columnNames.[idx] 
      let ty, converter = resultTypes.[idx] 
      let prop = ProvidedProperty(col, ty) 
      prop.GetterCode <- fun [row] -> converter <@@ (%%row:string[]).[idx] @@> 
      rowType.AddMember(prop) 

     let wholeFileType = ProvidedTypeDefinition(asm, ns, tyName, Some(typedefof<seq<_>>.MakeGenericType(rowType))) 
     wholeFileType.AddMember(rowType) 

     let ctor = ProvidedConstructor(parameters = []) // the *type* is parameterized but the *constructor* gets no args 
     ctor.InvokeCode <- //given the inputs, what will we get as the outputs? Now we want to read the *data*, skip the header 
      fun [] -> <@@ System.IO.File.ReadLines(filename) |> Seq.skip 1 |> Seq.map (fun line -> line.Split(',')) @@> 
     wholeFileType.AddMember(ctor) 
     wholeFileType 
     ) 

    do base.AddNamespace(ns, [csvFileProviderType]) 

[<TypeProviderAssembly>] 
do() 

Grazie per qualsiasi aiuto!

risposta

6

è necessario utilizzare un altro costruttore quando si definisce il tipo 'Riga'. Esistente tipo ProvidedTypeDefinition espone due costruttori:

  • (montaggio, namespace, typename, tipo base) - definisce il tipo di livello superiore il cui contenitore è namespace.
  • (typename, basetype) - definisce il tipo annidato che dovrebbe essere aggiunto ad un altro tipo.

Ora il tipo di riga viene definito utilizzando il primo costruttore, quindi viene considerato come un tipo di livello superiore. L'eccezione viene sollevata quando questo tipo viene successivamente aggiunto a wholeFileType come nidificato.

+0

Grazie! Apprezzo l'aiuto. –