[Update: This blog post was orignally posted in early 2008, in another blog. Things have changed a little in F#, and Ted Nedward has provided updated information related to this, showing how to create data contracts in F#]
On the road to a really interesting Web Service, I increased the complexity of my "could-not-be-simpler" web service one notch, and added a class-typed argument to the operation. This requires constructing of a Data Contract, so that WCF can build the correct WSDL, and also know how to serialize (and de-serialize) messages. As with the Service Contract, this is simply done with a couple of attributes, like this:
[<DataContract>]
type public SimpleDataContract() =
let mutable _propertyOne = System.String.Empty
let mutable _propetyTwo = 0
[<DataMember>]
member public x.PropertyOne
with get() = _propertyOne
and set(value) = _propertyOne <- value
[<DataMember>]
member public x.PropertyTwo
with get() = _propetyTwo
and set(value) = _propetyTwo <- value
[<ServiceContract(ConfigurationName = "ISimpleService", Namespace = "http://coyote-software.com/FSWCF/SimpleService")>]
type ISimpleService =
[<OperationContract>]
abstract TestMethod: Param:SimpleDataContract -> string
[<ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)>]
type SimpleService() =
interface ISimpleService with
member x.TestMethod s = "Hello " + s.PropertyOne;
Unfortunately, code causes a run-time exception when the service is started (well before any messages arrive). The error is about "method get_PropertyOne" not being a property, and therefore an invalid recipient of the DataMember attribute. I strongly suspect that this is due to a know bug in the current F# compiler emitting IL for properties in a non-standard way.
(WCF data contract members must be placed on properties, and not fields.)
The obvious work-around is to create the class in C#. The equivalent C# class is simply:
[DataContract]
public class SimpleDataContract
{
public SimpleDataContract() { }
[DataMember]
public string PropertyOne { get; set; }
[DataMember]
public int PropertyTwo { get; set; }
}
For once, C# trumps F# for brevity! Anyway, using this class from F# is no more complex than referencing the dll, and removing the F# SimpleDataContract class, and the web service now works as expected.
The next step is to do something a little more interesting in the operation; use LINQ to SQL to query a database, and return some data. As it turns out, our data contracts will be again come via C#, so this issue will be by-passed.