2012-06-23 69 views
6

最近我參加了Keith Battochi關於類型提供程序的教程,其中他在MSDN教程中介紹了MiniCsv類型提供程序的變體。不幸的是,我的筆記本電腦不可用,所以我不得不用手寫下代碼。我相信我已經重新類型供應商,但我越來越F#自定義類型提供程序:「容器類型已設置」錯誤

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

當我在看代碼,我看不到我如何加入行類型的容器兩次(或一些其他容器)。刪除代碼的選定行不起作用。

這裏是我如何調用代碼FSI:

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

而這裏的代碼本身:

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

感謝您的幫助!

回答

6

定義「行」類型時,您需要使用另一個構造函數。現有ProvidedTypeDefinition類型公開兩個構造:

  • (組件,命名空間,類型名稱,基類型) - 定義頂級類型,其容器是命名空間。
  • (typename,basetype) - 定義應該添加到某個其他類型的嵌套類型。

現在行類型是使用第一個構造函數定義的,因此它被視爲頂級類型。當此類型稍後添加到wholeFileType作爲嵌套時引發異常。

+0

謝謝!感謝幫助。 –