One of my goals this year is to get better at F#, by using it more, so I decided to port a simple WCF service over to F#. In this post, I will demonstrate how to port data and operation contracts over from C# to F#. Let’s start by looking at the service contract we will be porting (simplified for the purposes of this post):
[ServiceContract] public interface IRetrieval { [OperationContract] [FaultContract(typeof(RetrievalServiceFault))] ChunkResponse GetChunk(ChunkRequest request); [OperationContract] [FaultContract(typeof(RetrievalServiceFault))] VersionInfoResponse GetVersionInfo(VersionInfoRequest request); } [DataContract] public class VersionInfoResponse { [DataMember] public string Version { get; set; } } [DataContract] public class VersionInfoRequest { } [DataContract] public class ChunkResponse { [DataMember] public byte[] Data { get; set; } [DataMember] public bool IsEndOfFile { get; set; } } [DataContract] public class ChunkRequest { [DataMember] public string FileName { get; set; } [DataMember] public long Offset { get; set; } [DataMember] public int BytesRequested { get; set; } } [DataContract] public class RetrievalServiceFault { public RetrievalServiceFault(string message) { this.Message = message; } [DataMember] public string Message { get; private set; } }
Attributes
First, we need to know how to put attributes on things in F#. The syntax is similar to C#, just with an extra set of angle brackets (one of the few cases where C# is more compact than F#):
[<OperationContract>]
That’s easy, but what about the FaultContract attribute we have on each operation? That takes a type as a parameter. Well F# also has a typeof function, and you put the type name in angle brackets like so:
[<FaultContract(typeof<RetrievalServiceFault>)>]
Interfaces
Now we need to know how to declare an interface in F#. There seems to be no special syntax other than to declare a type containing only abstract members. For each method, we need to provide the name, the annotated parameter list, and the type it returns. The syntax looks a little odd to C# developers at first as we are used to the return type coming at the beginning rather than the end of the method signature. It takes the form abstract MethodName : ParameterName : ParameterType –> MethodReturnType.
Here’s our example
[<ServiceContract()>] type IRetrieval = [<OperationContract>] [<FaultContract(typeof<RetrievalServiceFault>)>] abstract GetChunk : request : ChunkRequest -> ChunkResponse [<OperationContract>] [<FaultContract(typeof<RetrievalServiceFault>)>] abstract GetVersionInfo : request : VersionInfoRequest -> VersionInfoResponse
Data Contracts
The final piece of the puzzle is to implement the four objects that are passed as the input and output to the methods on our interface. One of the challenges is that the properties need public getters and setters, and F# likes to make properties immutable. There are a few ways of achieving this in F#, but the simplest one for this purpose seems to be to use an F# record with the mutable keyword like so:
[<DataContract>] type VersionInfoResponse = { [<DataMember>] mutable Version : string }
Empty Classes
The VersionInfoRequest
class had me stumped for a while, as it contains no members at all (probably a bad design choice in C#), but I eventually stumbled on a way to implement this in F#:
[<DataContract>] type VersionInfoRequest() = do()
Longs and Byte Arrays
The final challenge for me was learning how to declare the types of C# long
and byte[]
types. In F# these turn into int64
and array<byte>
respectively. Other types, such as bool
, int
and string
, are unchanged from C#:
[<DataContract>] type ChunkResponse = { [<DataMember>] mutable Data : array<byte>; [<DataMember>] mutable IsEndOfFile : bool; } [<DataContract>] type ChunkRequest = { [<DataMember>] mutable FileName : string; [<DataMember>] mutable Offset : int64; [<DataMember>] mutable BytesRequested : int; }
I’ll hopefully find some time soon to do a follow-up post showing how to configure the WCF client and server in F#.
No comments:
Post a Comment