I'm trying to build a type provider for Bloomberg (they provide schema xml files that describe the types). I'm able to generate types that look like the following class:
type Example(x: option<int>) as self =
[<DefaultValue>] val mutable X: option<int>
do
self.X <- x
member this.GetX with get() = self.X
The problem is that this requires you to instantiate the example type as follows:
Example(Some 1)
Ideally I would prefer to instantiate it as like the XmlProvider choice type:
type Example(x: int) as self =
[<DefaultValue>] val mutable X: option<int>
do
self.X <- Some x
member this.GetX with get() = self.X
let example = Example(1)
When I attempt doing this:
let some =
FSharpType.GetUnionCases(field.FieldType)
|> Array.filter (fun x -> x.Name = "Some")
|> Array.exactlyOne
let arg' = Expr.NewUnionCase(some, arg)
let setValue = Expr.FieldSet(this, field, arg')
I get the following error:
"Specified method is not supported."
I can successfully generate the first type using this:'
let setValue = Expr.FieldSet(this, field, arg)
The full source code is available here: https://github.com/alphaarchitect/BloombergProvider/blob/main/src/BloombergProvider.DesignTime/BloombergProvider.DesignTime.fs#L457
Any ideas?
///Edit
The entire code block is from this:
es
|> Result.mapError (fun xs -> ChoiceError.Element(c.Name, xs))
|> Result.map (fun xs ->
xs
|> List.iter (fun (_, field) ->
let parameter = ProvidedParameter(field.Name, field.FieldType)
providedChoiceType.AddMember
<| ProvidedConstructor(
[parameter],
invokeCode =
fun args ->
match args with
| this :: [arg] ->
let setValue = Expr.FieldSet(this, field, arg)
let enumField = Expr.FieldSet(this, enumField, Expr.FieldGet(enum.GetField(field.Name)))
Expr.Sequential(enumField, setValue)
| _ -> failwith "wrong ctor parameters"))
{
Enum = enum
Object = providedChoiceType
}))
to this:
es
|> Result.mapError (fun xs -> ChoiceError.Element(c.Name, xs))
|> Result.map (fun xs ->
xs
|> List.iter (fun (type', field) ->
let parameter = ProvidedParameter(field.Name, type')
providedChoiceType.AddMember
<| ProvidedConstructor(
[parameter],
invokeCode =
fun args ->
match args with
| this :: [arg] ->
let some =
FSharpType.GetUnionCases(field.FieldType)
|> Array.filter (fun x -> x.Name = "Some")
|> Array.exactlyOne
let arg' = Expr.NewUnionCase(some, arg)
let setValue = Expr.FieldSet(this, field, arg')
let enumField = Expr.FieldSet(this, enumField, Expr.FieldGet(enum.GetField(field.Name)))
Expr.Sequential(enumField, setValue)
| _ -> failwith "wrong ctor parameters"))
{
Enum = enum
Object = providedChoiceType
}))
The error occurs when you try to instantiate the type provider.
Running and evaluating the code works correctly in the quotation evaluator.
This is not a direct answer to the question, but it may help you if you cannot figure out how to get this to work. In general, I think that building type providers is easier if you do not try to do a "lot of coding" in the provided methods and constructors.
In your approach, you have a type that inherits from
obj
and so you have to generate a lot of code to store data in fields. In contrast, the JSON provider types inherit fromJsonValue
which does all the storing for them - so they have to do relatively little. For example, say you want to generate something like:It may be easier to define a base class which is essentially a dictionary an then generate a class inheriting from this:
This way, the only kind of provided code that you have to write is method calls, which is generally easier to do - and possibly less error-prone.