I’ve been building an FSharp Dashboard by following along this post from Louie Bacaj’s which was part of last years FSharp Advent calendar. I have to say it’s a great post and has got me up and running in no time.
If you want to skip the story and get to the FSharp and SignalR part scroll down to Changing the Hub.
One small problem I noticed was that I could not use any of the features of FSharp Core v4. For example, the new
tryXXX functions such as
Array.tryLast were not available.
After a bit of digging I happened across the Project Properties which were stuck on
Turns out that the
FSharp.Interop.Dynamic package is dependant on
So this turned into a challenge of how do I use SignalR without Dynamic. After a bit of googling I landed on this page that showed Strongly Typed Hubs. So I knew it was possible…
The first step to fixing this was to remove the
FSharp.Core dependencies I no longer needed, these were:
Uninstall-Package FSharp.Interop.Dynamic Uninstall-Package Dynamitey Uninstall-Package FSharp.Core
I then just browsed through the source and removed all the
Re-adding FSharp Core
Slight problem now, I no longer had any FSharp Core references, so I needed to add one in. I’m not sure if this is the best way to solve this, but I just copied and pasted these lines from a empty FSharp project I just created:
<Reference Include="mscorlib" /> <!--Add this bit--> <Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <Private>True</Private> </Reference> <!--End--> <Reference Include="Newtonsoft.Json">
Changing the Hub
Now all I had to do was update the code to use the statically typed hub.
First step was to create an interface for the
type IMetricsHub = abstract member AddMessage: string -> unit abstract member BroadcastPerformance: PerfModel seq -> unit
Then change our
Hub to inherit from the generic
[<HubName("metricsHub")>] type metricsHub() = inherit Hub<IMetricsHub>() // < Generic version of our interface.
And changed all the calls from:
Getting the Context
With SignalR you cannot just
new up an instance of a
Hub, you have to use
GlobalHost.ConnectionManager.GetHubContext<THub>. The problem is that this gives you
IHubContext which only exposes the dynamic interface again. A bit more googling and I found that you need to pass our interface as a second generic parameter and you will get an
let context = GlobalHost.ConnectionManager.GetHubContext<metricsHub>()
let context = GlobalHost.ConnectionManager.GetHubContext<metricsHub, IMetricsHub>()
Now you can call
Context.Clients.All.BroadcastPerformance and not worry about that pesky dynamic any more.
The documentation on SignalR isn’t very good, it was easy enough to find out about the statically typed version, but finding out how to get one out of the context was a right pain.
I’ve published a fork of Louies GitHub repo with four commits that show the steps needed to move from dynamic to statically typed SignalR here so you can see the changes I needed to make.