Tuesday, 6 January 2015

Porting WCF Service Contracts to F#

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: