2012-06-23 8 views
6

Recientemente asistí a un tutorial de Keith Battochi sobre proveedores de tipos, en el que presentó una variante del proveedor de tipo MiniCsv en el tutorial de MSDN. Lamentablemente, mi computadora portátil no estaba disponible, así que tuve que escribir el código a mano lo mejor que pude. Creo que he recreado el tipo de proveedor, pero yo estoyF # proveedor de tipo personalizado: error "Tipo de contenedor ya configurado"

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

Cuando miro el código, no puedo ver cómo estoy añadiendo Tipo de fila en el recipiente dos veces (o hasta cierto otro contenedor). Eliminar las líneas seleccionadas del código no ayuda.

Así es como voy a llamar el código en FSI:

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

Y aquí está el código en sí mismo:

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

Gracias por cualquier ayuda!

Respuesta

6

necesita utilizar otro constructor al definir el tipo 'Fila'. El tipo ProvidedTypeDefinition existente expone dos constructores:

  • (ensamblado, espacio de nombres, nombre tipo, tipo base): define el tipo de nivel superior cuyo contenedor es el espacio de nombres.
  • (typename, basetype) - define el tipo anidado que se debe agregar a algún otro tipo.

Ahora el tipo de fila se define utilizando el primer constructor, por lo que se trata como tipo de nivel superior. Se genera una excepción cuando este tipo se agrega más tarde a wholeFileType como anidado.

+0

Gracias! Apreciar la ayuda. –

Cuestiones relacionadas