I recently visited Keith Battochi's tutorial on type providers, in which he introduced a variant of the type provider MiniCsv in the MSDN tutorial. Unfortunately, my laptop was not available, so I had to write the code manually as I could. I believe that I recreated the type provider, but I get
error FS3033: The type provider 'CsvFileTypeProvider+CsvFileTypeProvider' reported an error: container type for 'CsvFileProvider.Row' was already set to 'CsvFileProvider.CsvFile,Filename="events.csv"
When I look at the code, I donβt see how I add the Row type to the container (or to another container) twice. Deleting selected lines of code does not help.
This is how I call the code in fsi:
#r "CsvFileTypeProvider.dll" open CsvFileProvider let eventInfos = new CsvFile<"events.csv">() ;;
And here is the code itself:
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()
Thanks for any help!