Run method on every generic record type in the assembly - f#

Getting into the bowels now. Hopefully this is the last time I have to deal with Reflection for a while and I can return to the high level where I belong.
I have a type "PrimaryKey" defined as such.
type PrimaryKey<'x> =
| Id of int
| EmptyPrimaryKey
Then a bunch of record types associated with database tables, for example:
type User = {
user_id: PrimaryKey<User>
username: string
email: string
address_id: PrimaryKey<Address>
} with
static member DatabaseTable = "users"
I have written a custom type handler for Dapper to handle the Primary Key type.
type PrimaryKeyHandler<'X>() =
inherit SqlMapper.TypeHandler<PrimaryKey<'X>>()
(* I don't think this works but that's a future problem *)
override _.SetValue(param, value) =
let valueOrNull =
match value with
| PrimaryKey.Id id -> box id
| EmptyPrimaryKey -> null
param.Value <- valueOrNull
override _.Parse value =
if isNull value || value = box DBNull.Value
then EmptyPrimaryKey
else Id (value :?> int)
Now the problem with that is I have to call::
SqlMapper.AddTypeHandler (PrimaryKeyHandler<User>())
SqlMapper.AddTypeHandler (PrimaryKeyHandler<OtherRecordType>())
On every record that has a primary key. I'm basing my solution off of this: Dapper generic typehandler for F# Union types
But I don't understand it enough to adapt it to my needs, I think I need extra handling for the generic type.
What I've started with is this:
let RegisterTypeHandlers () =
let assembly = Assembly.GetCallingAssembly()
let handler = typedefof<PrimaryKeyHandler<_>>
assembly.GetTypes()
|> Seq.filter(fun t ->
FSharpType.IsRecord(t) && t.GetProperty("DatabaseTable") <> null
)
Which successfully returns a list of record types which have database table associations.
However trying to iterate over that list and call AddTypeHandler on all of those types fails:
let RegisterTypeHandlers () =
let assembly = Assembly.GetCallingAssembly()
let handler = typedefof<PrimaryKeyHandler<_>>
assembly.GetTypes()
|> Seq.filter(fun t ->
FSharpType.IsRecord(t) && t.GetProperty("DatabaseTable") <> null
)
|> Seq.iter(fun t ->
printfn $"Type: {t.Name}"
let ctor = handler
.MakeGenericType(t)
.GetConstructor(Array.empty)
.Invoke(Array.empty)
(typeof<SqlMapper>.GetMethods()
|> Seq.filter(fun methodInfo ->
if methodInfo.Name = "AddTypeHandler" && methodInfo.IsGenericMethodDefinition then
let gp = methodInfo.GetParameters()
not <| isNull gp && gp.Length = 1 && gp.[0].ParameterType.Name.Contains("TypeHandler")
else false)
|> Seq.head)
.MakeGenericMethod(t)
.Invoke(null, [| ctor |]) |> ignore
)
The error being
Unhandled exception. System.ArgumentException: Object of type 'MyModule.Common+PrimaryKeyHandler`1[Program+User]' cannot be converted to type 'Dapper.SqlMapper+TypeHandler`1[Program+User]'.
I've been looking at some of the GenericType functions in reflection but not really sure where to go from here.

First of all, you are getting an error saying that PrimaryKeyHandler<User> cannot be converted to type TypeHandler<User>. This is correct, because your type PrimaryKeyHandler<User> inherits from TypeHandler<PrimaryKey<User>>.
I think this happens because you get the AddTypeHandler method via reflection and then use MakeGenericMethod(t) to make it generic - but if t is User, then you get the wrong generic instantiation - you need to wrap t with PrimaryKey<..> around it first.
I have not tested this, but I think the following should work:
let addTyMi =
typeof<SqlMapper>.GetMethods()
|> Seq.find(fun methodInfo ->
if methodInfo.Name = "AddTypeHandler" &&
methodInfo.IsGenericMethodDefinition then
let gp = methodInfo.GetParameters()
not <| isNull gp && gp.Length = 1 &&
gp.[0].ParameterType.Name.Contains("TypeHandler")
else false)
let pkt = typedefof<PrimaryKey<_>>.MakeGenericType(t)
addTyMi.MakeGenericMethod(pkt).Invoke(null, [| ctor |]) |> ignore
It seems to me that there is also a non-generic overload of AddTypeHandler taking System.Type (by browsing GitHub source - I have not tried this). Maybe you could do just:
let pkt = typedefof<PrimaryKey<_>>.MakeGenericType(t)
SqlMapper.AddTypeHandler(pkt, ctor)
...avoiding some of the reflection. Also, ctor is a bad name, because the variable refers to the instance!

Related

Dapper generic typehandler for F# Union types

I'm using union types similar to enums on my dapper objects:
type Confidence =
| Low
| Medium
| High
type Goal = {
Confidence: Confidence
...
}
I've created a custom type handler in order to make it work:
type UnionHandler<'T>() =
inherit SqlMapper.TypeHandler<'T>()
override __.SetValue(param, value) =
param.Value <- value.ToString()
()
override x.Parse(value: obj) =
Union.parse <| string value
let registerTypeHandlers() =
SqlMapper.AddTypeHandler (UnionHandler<Confidence>())
This works fine, but it would be even nicer if I didn't have to register a new one for each new union type.
Is it possible to make the type handler generic in such a way that it can handle all union types with only one registration?
This can be done with Reflection:
let internal addUnionTypeHandlers() =
let assembly = Assembly.GetExecutingAssembly()
let unionHandlerType =
assembly.GetTypes()
|> Seq.filter(fun t -> t.Name.Contains("UnionHandler") && t.IsGenericTypeDefinition)
|> Seq.head
assembly.GetTypes()
|> Seq.filter(fun t -> not t.IsGenericType && FSharpType.IsUnion(t, BindingFlags.Default))
|> Seq.iter(fun t ->
let ctor = unionHandlerType
.MakeGenericType(t)
.GetConstructor(Array.empty)
.Invoke(Array.empty)
(typeof<SqlMapper>.GetMethods()
|> Seq.filter(fun methodInfo ->
if methodInfo.Name = "AddTypeHandler" && methodInfo.IsGenericMethodDefinition then
let gp = methodInfo.GetParameters()
not <| isNull gp && gp.Length = 1 && gp.[0].ParameterType.Name.Contains("TypeHandler")
else false)
|> Seq.head)
.MakeGenericMethod(t)
.Invoke(null, [| ctor |]) |> ignore
)
Note:
It would have been much simpler if Dapper have had the signature of AddTypeHandler in a form ITypeHandler -> unit. But it accepts TypeHandler and in addition has overloaded version. So we need GMD for method AddTypeHandler and instantiate it with method MakeGenericMethod and then call this method with parameter which we obtains from GetConstructor ... Invoke
Playing further with reflection you can decide to mark some discriminated unions with some attribute to ignore adding the mapping. You can extend code to analyse if type has attribute. Also you can do manipulations on module basis I assume using FSharpType.IsModule

How to downcast from obj to option<obj>?

I have a function that takes a parameter of type object and needs to downcast it to an option<obj>.
member s.Bind(x : obj, rest) =
let x = x :?> Option<obj>
If I pass (for example) an Option<string> as x, the last line throws the exception: Unable to cast object of type 'Microsoft.FSharp.Core.FSharpOption'1[System.String]' to type 'Microsoft.FSharp.Core.FSharpOption'1[System.Object]'.
Or, if I try a type test:
member s.Bind(x : obj, rest) =
match x with
| :? option<obj> as x1 -> ... // Do stuff with x1
| _ -> failwith "Invalid type"
then x never matches option<obj>.
In order to make this work, I currently have to specify the type the option contains (e.g. if the function is passed an option<string>, and I downcast the parameter to that rather than option<obj>, the function works.
Is there a way I can downcast the parameter to option<obj> without specifying what type the option contains? I've tried option<_>, option<#obj>, and option<'a> with the same results.
By way of background, the parameter needs to be of type obj because I'm writing an interface for a monad, so Bind needs to bind values of different types depending on the monad that implements the interface. This particular monad is a continuation monad, so it just wants to make sure the parameter is Some(x) and not None, then pass x on to rest. (The reason I need the interface is because I'm writing a monad transformer and I need a way to tell it that its parameter monads implement bind and return.)
Update: I managed to get around this by upcasting the contents of the option before it becomes a parameter to this function, but I'm still curious to know if I can type-test or cast an object (or generic parameter) to an option without worrying about what type the option contains (assuming of course the cast is valid, i.e. the object really is an option).
There isn't any nice way to solve this problem currently.
The issue is that you'd need to introduce a new generic type parameter in the pattern matching (when matching against option<'a>), but F# only allows you to define generic type parameters in function declarations. So, your only solution is to use some Reflection tricks. For example, you can define an active pattern that hides this:
let (|SomeObj|_|) =
let ty = typedefof<option<_>>
fun (a:obj) ->
let aty = a.GetType()
let v = aty.GetProperty("Value")
if aty.IsGenericType && aty.GetGenericTypeDefinition() = ty then
if a = null then None
else Some(v.GetValue(a, [| |]))
else None
This will give you None or Some containing obj for any option type:
let bind (x : obj) rest =
match x with
| SomeObj(x1) -> rest x1
| _ -> failwith "Invalid type"
bind(Some 1) (fun n -> 10 * (n :?> int))
I am not certain why you need to get your input as obj, but if your input is an Option<_>, then it is easy:
member t.Bind (x : 'a option, rest : obj option -> 'b) =
let x = // val x : obj option
x
|> Option.bind (box >> Some)
rest x
To answer your last question: you can use a slight variation of Tomas' code if you need a general-purpose way to check for options without boxing values beforehand:
let (|Option|_|) value =
if obj.ReferenceEquals(value, null) then None
else
let typ = value.GetType()
if typ.IsGenericType && typ.GetGenericTypeDefinition() = typedefof<option<_>> then
let opt : option<_> = (box >> unbox) value
Some opt.Value
else None
//val ( |Option|_| ) : 'a -> 'b option
let getValue = function
| Option x -> x
| _ -> failwith "Not an option"
let a1 : int = getValue (Some 42)
let a2 : string = getValue (Some "foo")
let a3 : string = getValue (Some 42) //InvalidCastException
let a4 : int = getValue 42 //Failure("Not an option")

how do i fix these errors generated by my computational expression that is using my custom workflow builder?

From the MSDN documentation I understand that if Run is implemented it will be called automatically at the end of the computational expression. It says that:
builder.Run(builder.Delay(fun () -> {| cexpr |}))
will be generated for the computational expression. Run and/or Delay will be omitted if they are not defined in the workflow builder. I was expecting my ReaderBuilder to return a list of MyItem objects when Run is called automatically. So I do not understand why I'm getting a type mismatch error. The errors are generated by the return statement inside the ProcedureBuilder foo at the end of my code listing here. Could someone please explain what I'm misunderstanding about workflow builders and what I have implemented incorrectly?
I'm getting the following errors:
The type ''a list' is not compatible with the type 'ReaderBuilder'
Type constraint mismatch. The type 'a list is not compatible with type ReaderBuilder The type ''a list' is not compatible with the type 'ReaderBuilder'
open System
open System.Data
open System.Data.Common
open System.Configuration
let config = ConfigurationManager.ConnectionStrings.Item("db")
let factory = DbProviderFactories.GetFactory(config.ProviderName)
type Direction =
| In
| Out
| Ref
| Return
type dbType =
| Int32
| String of int
type ReaderBuilder(cmd) =
let mutable items = []
member x.Foo = 2
member x.YieldFrom item =
items <- item::items
item
member x.Run item =
items
type ProcBuilder(procedureName:string) =
let name = procedureName
let mutable parameters = []
let mutable cmd:DbCommand = null
let mutable data = []
member x.Command with get() = cmd
member x.CreateCommand() =
factory.CreateCommand()
member x.AddParameter(p:string*dbType*Direction) =
parameters <- p::parameters
member x.Bind(v,f) =
f v
member x.Reader = ReaderBuilder(cmd)
member x.Return(rBuilder:ReaderBuilder) =
data
let (?<-) (builder:ProcBuilder) (prop:string) (value:'t) =
builder.Command.Parameters.[prop].Value <- value
type MyItem() =
let mutable _a = 0
let mutable _b = String.Empty
let mutable _c = DateTime.Now
member x.a
with get() = _a
and set n = _a <- n
member x.b
with get() = _b
and set n = _b <- n
member x.c
with get() = _c
and set n = _c <- n
let proc name = ProcBuilder(name)
let (%) (builder:ProcBuilder) (p:string*dbType*Direction) =
builder.AddParameter(p)
builder
let (?) (r:DbDataReader) (s:string) = r.GetOrdinal(s)
let foo x y =
let foo = proc "foo" % ("x", Int32, In) % ("y", String(15), In)
foo?x <- x
foo?y <- y
foo {
do! foo?x <- x
do! foo?y <- y
return foo.Reader {
let item = MyItem()
item.a <- r.GetInt32("a")
item.b <- r.GetString("b")
item.c <- r.GetDateTime("c")
yield! item
}
}
The problem in your example is that the foo.Reader { ... } block has a return type MyItem list (because this is what the Run member of the ReaderBuilder type returns). However, the Return member of ProcBuilder expects an argument of type ReaderBuilder.
The data field of ReaderBuilder will be always an empty list, so this is also suspicious. I think you probably want to change the Return of ProcBuilder to take an argument MyItem list instead.
However, I think that using custom computation builder for database access doesn't really give you much advantage. You're not creating a "non-standard computation" in some sense. Instead, you probably just want a nice syntax for calling commands & reading data. Using the dynamic operator can make this quite elegant even without computation builders - I wrote an article about this some time ago.

How to check if an event is being handled in F#

What is the F# equivalent of the following C# code? Specifically, I need to check if an event is being handled.
protected virtual void OnClicked(ClickEventArgs e) {
if (this.Clicked != null) //how can I perform this check in F#
this.Clicked(this, e);
}
Okay, I think I figured this thing out. Taking a cue from Don Syme's blog, specifically the section "The Implementation of the IEvent Module."
Instead of the following:
let validationFailedEvent = new Event<DataValidationEventHandler, DataValidationEventArgs>()
I had to implement IEvent myself and create a variable to hold the invocation list:
let mutable listeners: Delegate = null
let validationFailedEvent = { new IEvent<DataValidationEventHandler, DataValidationEventArgs> with
member x.AddHandler(d) =
listeners <- Delegate.Combine(listeners, d)
member x.RemoveHandler(d) =
listeners <- Delegate.Remove(listeners, d)
member x.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(x :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (x :?> IEvent<_,_>).RemoveHandler(h) } }
Then, to check if there are listeners, and, if not, raise an exception:
member private x.fireValidationFailedEvent(e:DataValidationEventArgs) =
match listeners with
| null -> failwith "No listeners"
| d -> d.DynamicInvoke([| box x; box e |])
An alternative way to implement RequiresSubscriptionEvent is to build on top of the existing Event functionality (using composition) and just add a counter that counts the number of registered handlers and add a property HasListeners (or even publish the number of listeners if you wanted...)
This makes the code a bit easier to use and hopefuly also safer, because if you don't check whether it has any listneres, it will still work as the usual F# code. And if you want to perform the check, you can...
type RequiresSubscriptionEvent<_>() =
let evt = new Event<_>()
let mutable counter = 0
let published =
{ new IEvent<_> with
member x.AddHandler(h) =
evt.Publish.AddHandler(h)
counter <- counter + 1;
member x.RemoveHandler(h) =
evt.Publish.RemoveHandler(h)
counter <- counter - 1;
member x.Subscribe(s) =
let h = new Handler<_>(fun _ -> s.OnNext)
x.AddHandler(h)
{ new System.IDisposable with
member y.Dispose() = x.RemoveHandler(h) } }
member x.Trigger(v) = evt.Trigger(v)
member x.Publish = published
member x.HasListeners = counter > 0
Sample usage:
type Demo() =
let evt = new RequiresSubscriptionEvent<_>()
[<CLIEvent>]
member x.OnSomething = evt.Publish
member x.FooThatFiresSomething() =
if evt.HasListeners then
evt.Trigger("foo!")
else
printfn "No handlers!"
Even though this isn't a part of standard F# libraries, it shows the great advantage of F# first class events. If there is some missing functionality, you can simply implement it yourself!
Typically, you don't need to do that check in F# (the event infrastructure checks for you):
type T() =
let ev = new Event<_>()
[<CLIEvent>]
member x.Event = ev.Publish
member x.OnClicked() =
ev.Trigger()
I followed kvb's suggestion and put this logic in a class. I copied Event from the F# sources and added a Handled property, which checks if the Delegate is null. I tried adding to, then removing handlers from the event to make sure it gets set back to null, and indeed it does.
type EventEx<'Delegate,'Args when 'Delegate : delegate<'Args,unit> and 'Delegate :> System.Delegate >() =
let mutable multicast : System.Delegate = null
static let argTypes =
let instanceBindingFlags = BindingFlags.Instance ||| BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.DeclaredOnly
let mi = typeof<'Delegate>.GetMethod("Invoke",instanceBindingFlags)
mi.GetParameters() |> (fun arr -> arr.[1..]) |> Array.map (fun p -> p.ParameterType)
member x.Handled = (multicast <> null)
member x.Trigger(sender:obj,args:'Args) =
match multicast with
| null -> ()
| d ->
if argTypes.Length = 1 then
d.DynamicInvoke([| sender; box args |]) |> ignore
else
d.DynamicInvoke(Array.append [| sender |] (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields(box args))) |> ignore
member x.Publish =
{ new IEvent<'Delegate,'Args> with
member x.AddHandler(d) =
multicast <- System.Delegate.Combine(multicast, d)
member x.RemoveHandler(d) =
multicast <- System.Delegate.Remove(multicast, d)
member e.Subscribe(observer) =
let h = new Handler<_>(fun sender args -> observer.OnNext(args))
(e :?> IEvent<_,_>).AddHandler(h)
{ new System.IDisposable with
member x.Dispose() = (e :?> IEvent<_,_>).RemoveHandler(h) } }
This article here http://geekswithblogs.net/Erik/archive/2008/05/22/122302.aspx says you do not need to check for null events in F#, though I don't know what his reference is.
This article http://blogs.msdn.com/dsyme/articles/FSharpCompositionalEvents.aspx by Don Symes goes into F# events in quite a bit of detail. It looks like events are not owned by the class in F#
From the above,
it is that events are now first-class
values in the F# langauge. Indeed,
events are not a separate notion at
all in the language design, rather,
events are just values of type
Microsoft.FSharp.Idioms.IEvent<_>, and
.NET events are effectively just
properties of this type.
And
One of the restrictions of C# is that
events can only exist as members
within classes. With the F# model,
new event values can be created just
as values as part of any expression.

Is it possible to use the pipeline operator to call a method on a returned object?

Is it possible to call a method on a returned object using the pipeline infix operator?
Example, I have a .Net class (Class1) with a method (Method1). I can currently code it like this:
let myclass = new Class1()
let val = myclass.Method1()
I know I could also code it as such
let val = new Class1().Method1()
However I would like to do be able to pipeline it (I am using the ? below where I don't know what to do):
new Class1()
|> ?.Method1()
Furthermore, say I had a method which returns an object, and I want to only reference it if that method didn't return null (otherwise bail?)
new Class1()
|> ?.Method1()
|> ?? ?.Method2()
Or to make it clearer, here is some C# code:
public void foo()
{
var myclass = new Class1();
Class2 class2 = myclass.Method1();
if (class2 == null)
{
return;
}
class2.Method2();
}
You can define something similar to your (??) operator fairly easily (but operators can't start with a question mark):
let (~??) f x =
if (x <> null) then
f x
Unfortunately, your pipelined code will need to be a bit more verbose (also, note that you can drop the new keyword for calling constructors):
Class1()
|> fun x -> x.Method1()
Putting it all together:
Class1()
|> fun x -> x.Method1()
|> ~?? (fun x -> x.Method2())
Using a custom operator as 'kvb' suggests is definitely an option. Another approach that you may find interesting in this case is to define your own 'computation expression' that automatically performs the check for null value at every point you specify. The code that uses it would look like this:
open System.Windows.Forms
// this function returns (0) null, or (1) btn whose parent is
// null or (2) button whose parent is not null
let test = function
| 1 -> new Button(Text = "Button")
| 2 -> new Button(Text = "Button", Parent = new Button(Text = "Parent"))
| _ -> null
let res =
safe { let! btn = test(2) // specify number here for testing
// if btn = null, this part of the computation will not execute
// and the computation expression immediately returns null
printfn "Text = %s" btn.Text
let! parent = btn.Parent // safe access to parent
printfn "Parent = %s" parent.Text // will never be null!
return parent }
As you can see, when you want to use a value that can potentially be 'null', you use let! inside the computation expression. The computation expression can be defined so that it immediately returns null if the value is null and runs the rest of the computation otherwise. Here is the code:
type SafeNullBuilder() =
member x.Return(v) = v
member x.Bind(v, f) =
if v = null then null else f(v)
let safe = new SafeNullBuilder()
BTW: If you want to learn more about this, it is very similar to 'Maybe' monad in Haskell (or computation working with F# option type).

Resources