F # TypeProvider Generating Types

I am trying to create a type provider that generates types and does not delete them. I used the GeneratedTypeProvider example and made my own version.

What I'm trying to do is that some type generates (like a fact) that has one constructor for the type and its properties (like Id, timestamp, name). I use the element type as the base type. The design should lead to an immutable fact object.

If I run it in an erasable version, everything will be fine, but I do not get the Fact type. When I make a non-erased version (setting Ty.IsErased <- false types and adding a type to the provided assembly provided by Assembly.AddTypes ([typesTy])), it no longer works.

I found that an extra argument is passed to the arguments to the constructor that I am providing. This is a type of fact. And the structure complains that for Fact there is no constructor. But what I provide is a constructor for Fact, including its properties.

What do I need to do to make this work?

My code is:

type Element(values: obj []) =
    let propertyMap = new Map<int, obj>(values |> Seq.mapi (fun i value -> (i, value)))
    member this.GetValue propertyIndex : obj = 
        match propertyMap.TryFind propertyIndex with
        | Some(value) -> value
        | None        -> box "property not found"

let private typeOf elementType =
    match elementType with
    | "String"   -> typeof<string>
    | "Guid"     -> typeof<System.Guid>
    | "DateTime" -> typeof<System.DateTime>
    | _ -> typeof<string>

let internal makeTypeWith thisAssembly namespaceName group entityName = 

    let entityType = 
        ProvidedTypeDefinition(thisAssembly, namespaceName,
                                entityName,
                                baseType = Some typeof<Element>)
    entityType.AddXmlDocDelayed (fun () -> sprintf "This %s" entityName)

    let properties = [("Id", "Guid"); ("Timestamp", "DateTime"); ("Name", "String")] 
    let fieldsOfProperties =
        properties 
        |> List.iteri (fun index (propertyName, propertyType) ->
            let instanceProp = 
                ProvidedProperty(propertyName = propertyName, 
                                 propertyType = typeOf propertyType, 
                                 GetterCode = (fun args -> <@@ unbox ((%%(args.[0]) : Element).GetValue index) @@>))
            instanceProp.AddXmlDocDelayed(fun () -> sprintf "%s" propertyName)
            entityType.AddMember instanceProp)

    let typeConstructor = 
        ProvidedConstructor(
            parameters = 
                (properties 
                    |> List.mapi (fun index (name, typ) -> ProvidedParameter(parameterName = name, parameterType = typeOf typ))),
            InvokeCode = 
                (fun args ->  
                    let boxedArgs = 
                        args |> List.map (fun arg -> 
                            match arg with
                            | Quotations.Patterns.Var var -> 
                                if var.Type = typeof<int> then 
                                    <@@ (box (%%arg: int)) @@>
                                else if var.Type = typeof<string> then 
                                    <@@ (box (%%arg: string)) @@>
                                else if var.Type = typeof<System.Guid> then 
                                    <@@ (box (%%arg: System.Guid)) @@>
                                else if var.Type = typeof<System.DateTime> then 
                                    <@@ (box (%%arg: System.DateTime)) @@>
                                else 
                                    let argsVals = 
                                        args |> List.map (fun arg -> 
                                            match arg with
                                            | Quotations.Patterns.Var var -> var.Type.ToString()
                                            | _ -> "unknown")
                                        |> List.reduce (fun all arg -> all + ", " + arg)                                                
                                    failwith ("Aha: " + argsVals)
                            | _ -> failwith ("Unknown Expr as parameter"))
                    <@@ Element(%%(Expr.NewArray(typeof<obj>, boxedArgs))) :> obj @@>))

    typeConstructor.AddXmlDocDelayed(fun () -> "This is the constructor")
    entityType.AddMember typeConstructor
    entityType

[<TypeProvider>]
type public DataLayerProvider(cfg:TypeProviderConfig) as this =
    inherit TypeProviderForNamespaces()

    let thisAssembly =  Assembly.GetExecutingAssembly()
    let rootNamespace = "Types"
    let providedAssembly = new ProvidedAssembly(System.IO.Path.ChangeExtension(System.IO.Path.GetTempFileName(), ".dll"))


    let typesTy = makeTypeWith thisAssembly rootNamespace "Some" "Fact"

    do 
        typesTy.IsErased <- false
        providedAssembly.AddTypes([typesTy])

    do System.AppDomain.CurrentDomain.add_AssemblyResolve(fun _ args ->
        let name = System.Reflection.AssemblyName(args.Name)
        let existingAssembly = 
            System.AppDomain.CurrentDomain.GetAssemblies()
            |> Seq.tryFind(fun a -> System.Reflection.AssemblyName.ReferenceMatchesDefinition(name, a.GetName()))
        match existingAssembly with
        | Some a -> a
        | None -> null)

    do this.AddNamespace(rootNamespace, [typesTy])


[<TypeProviderAssembly>]
do ()
+4
source share

All Articles