Service Interface
At the heart of this WCF solution is the Service Interface which describes the operations that our service supports. In addition, the service interface (contract) assembly also contains the DTOs and SOAP Faults that are to be exchanged over the wire. Personally, I like to organize this assembly by putting DTOs in a separate subfolder called DTOs, Enums in a separate subfolder called Enumerations, Faults in a separate subfolder called Faults, etcetera. All of the subfolders correspond to a subnamespace within the assembly.
Service Contract
So the service contract of our service interface will look like this:
using System.ServiceModel; using MathServiceInterface.DTOs; using MathServiceInterface.Faults; namespace MathServiceInterface { [ServiceContract] public interface IMathService { [OperationContract] [FaultContract(typeof(BinaryOperationFault))] double PerformBinaryOperation(BinaryOperation binaryOperation); [OperationContract] [FaultContract(typeof(SlopeCalculationFault))] double CalculateSlope(TwoDimensionalPoint point1, TwoDimensionalPoint point2); } }
In order to define the service contract, we must decorate our .NET interface with the ServiceContractAttribute. In order to indicate that a method is a part of the service contract, we must decorate it with the OperationContractAttribute. A more advanced feature shown here is specifying which SOAP faults can possibly be returned in the event that an error occurs while executing a service operation by specifying the fault types via the FaultContractAttribute.
Data Transfer Objects
Let’s look at an example of defining a DTO for our WCF service by viewing what the BinaryOperation DTO looks like:
using System.Runtime.Serialization; using MathServiceInterface.Enumerations; namespace MathServiceInterface.DTOs { [DataContract] public class BinaryOperation { [DataMember] public double Operand1 { get; set; } [DataMember] public double Operand2 { get; set; } [DataMember] public BinaryOpEnum Operation { get; set; } } }
The defined type contains two operands and an operation enumeration type (we will get to the enum declaration in just a moment). This is a fairly basic class, but the important aspect to note is the DataContractAttribute applied to the class which explicitly enables the DataContractSerializer to serialize and deserialize the data as it is exchanged between the client and server system. The properties of our type are also marked with the DataMemberAttribute which specifies that the member is a part of the data contract and is serializable by the DataContractSerializer.
Note that we could do without the DataContract and DataMember attributes and the WCF runtime would just default to serializing all public read/write properties and fields. However, it is generally considered best practice to explicitly mark the classes which are to be exchanged with the DataContract attribute and each desired member with the DataMember attribute – Not only does this make the code backwards compatible with the .NET Framework versions which did not have this ‘defaulting behavior’, but it also better describes the desired function of a given type.
Enumerations
Next, we have an enumeration defined in our service interface called BinaryOpEnum. Again, let’s take a quick look at defining enumerations for exchange within WCF by examining the definition of this enum type.
using System.Runtime.Serialization; namespace MathServiceInterface.Enumerations { [DataContract] public enum BinaryOpEnum { [EnumMember] Add = 0, [EnumMember] Subtract = 1, [EnumMember] Multiply = 2, [EnumMember] Divide = 3 } }
Here we apply the DataContractAttribute to the enum just like with the classes we want to use the DataContractSerializer serialization engine on. What is different is that we use the EnumMemberAttribute on each enum member we wish to have serialized and as a part of the data contract.
Faults
Lastly, let us look at defining fault types for our WCF service. Learning by example, we will examine the BinaryOperationFault type defined in the service.
using System.Runtime.Serialization; using MathServiceInterface.Enumerations; namespace MathServiceInterface.Faults { [DataContract] public class BinaryOperationFault { [DataMember] public BinaryOpEnum Operation { get; set; } [DataMember] public string Message { get; set; } public BinaryOperationFault(BinaryOpEnum operation, string msg) { Operation = operation; Message = msg; } } }
Here we define an Operation property which utilizes a BinaryOpEnum member to indicate the operation which generated the SOAP Fault. We also define a Message property which will provide a brief string description of what caused the error. We define a constructor to simplify generating the fault on the server side. Lastly, we mark the type as serializable by the DataContractSerializer.
Creating a service interface mainly requires knowledge of various attributes as well as design considerations centering around the fact that this is something shared between both sides of the wire.