I spent a large portion of my career working with WS-*/SOAP. So, once I understood how F# attributes worked, I thought it would be cool to see if I could write a WCF service. Nothing complex– I just wanted to see if I could get HelloWorld up and running. Given that one can get a class running as a service with the right config and .svc file, this seemed like an easy task. First, I’ll present the solution, then I’ll talk about a couple of issues I ran into along the way to the solution. In F#, one adds attributes by placing a [<Attribute>] before the type or member. For WCF, we need the ServiceContractAttribute and OperationContractAttribute on the type and any exposed functions. The HelloWorld service can be written like this:
1 #light
2
3 namespace Service
4 open System.ServiceModel
5
6 [<ServiceContract>]
7 type MyContract =
8 new() = {}
9 [<OperationContract>]
10 member x.HelloWorld(name: string)= “Hello, “ + name
Following the path most folks take, hosting in IIS, we next write a .svc file with the following content:
<%@ ServiceHost Service=”Service.MyContract” %>
This just tells the ServiceHostFactory to instantiate an object of type Service.MyContract whenever a request comes in. Finally, we go into web.config and add the following markup:
1 <system.serviceModel>
2 <behaviors>
3 <serviceBehaviors>
4 <behavior name=“SimpleService.SimpleServiceBehavior“>
5 <serviceMetadata httpGetEnabled=“true“/>
6 <serviceDebug includeExceptionDetailInFaults=“false“/>
7 </behavior>
8 </serviceBehaviors>
9 </behaviors>
10 <services>
11 <service behaviorConfiguration=“SimpleService.SimpleServiceBehavior“ name=“Service.MyContract“>
12 <endpoint address=“” binding=“wsHttpBinding“ contract=“Service.MyContract“>
13 <identity>
14 <dns value=“localhost“/>
15 </identity>
16 </endpoint>
17 <endpoint address=“mex“ binding=“mexHttpBinding“ contract=“IMetadataExchange“/>
18 </service>
19 </services>
20 </system.serviceModel>
Finally, I wrote a client application in C#, using Add Service Reference in VS2008 to generate the client code.
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 using (ServiceReference1.MyContractClient client = new SimpleClient.ServiceReference1.MyContractClient())
6 {
7 Console.WriteLine(client.HelloWorld(“Scott”));
8 }
9 }
10 }
And it all worked just fine. I know– it’s all .NET and that’s the way things should work. I’m most impressed by the terseness of F#.
OK, so what did I learn while doing this? First off, F# does NOT create a default, empty constructor for classes. WCF barked at me when I tried to access the SVC file, stating that it couldn’t create the object because the object lacked a default constructor. So, I dug into the F# spec and learned that a default, do nothing constructor looks like
new() = {}
I also tried doing this in the traditional way, with an interface definition first and then an implementation of the interface. That didn’t work so well. In F#, the interface looks like this:
[<ServiceContract>]
type IMyContract =
[<OperationContract>]
abstract HelloWorld : string -> string
That little bit on HelloWorld states that the input is a single string that yields a string. The input parameter does not have a name, and that’s a problem. No name means no name, null, in F#. When System.ServiceModel does its reflection thing to figure out what the name of the parameter in the message should be, it sees a null and throws up its hands. I was able to name the parameter only when applying the attributes on the named parameter in the implementation. So, I ditched the interface. Maybe I missed something that would allow me to name the parameter, but I’m not too sure what it could be. For now, I’ll assume that this was a legitimate issue. I’ll update this post if I learn otherwise.