WCF Crib Sheet – Configuring a Service and Hosting on IIS 5/6

WCF on IIS 5/6

  • IIS manages the lifecycle of the host processes.
  • IIS 5/6 only supports HTTP/HTTPS.

Hosting a WCF service to IIS

  • Create a virtual directory.
  • Create a .SVC file, detailing the service class and assembly, and deploy to the virtual directory.
<!--ProductService.svc-->
<%@ ServiceHost Language="C#" Debug="true" Service="BikeStore.WcfServiceLibrary.ProductService" %>
<%@ Assembly Name="BikeStore.WcfServiceLibrary" %>
  • It is good practice to implement the service in a WCF Service Library, keeping it separate from the service hosting application.

Configuring a WCF service

  • Create a web.config file and deploy to the virtual directory.

Services

  • Add a <service> element for each service, with the name attribute equal to the fully-qualified service class.
  • An optional behaviorConfiguration attribute can be used to assign a set of service behaviors to a service. If this attribute is missing or equal to “” then the service will assume the default set of behaviors
<system.serviceModel>
    <services>
      <service name="BikeStore.WcfServiceLibrary.ProductService">
        ...
      </service>
    </services>
</system.serviceModel>

Endpoints

  • For each service, add one or more <endpoint> elements, detailing addresses and binding protocols available to clients.
  • Each <endpoint> element has an address attribute specifying a URI for contacting the service. This can be an absolute address or one relative to the base address of the service.
  • Each <endpoint> has a binding attribute detailing the type of transport, security, and encoding. IIS 5/6 only supports Http bindings –
    • basicHttpBinding – backwards compatibility for ASMX (SOAP 1.1) clients. Supports HTTP and HTTPS, but does not incorporate WS-* standards.
    • wsHttpBinding – SOAP 1.2. Supports HTTP and HTTPS, and incorporates WS-Addressing, WS-Security and WS-ReliableMessaging.
<system.serviceModel>
  <services>
    <service name="BikeStore.WcfServiceLibrary.ProductService">
      <endpoint address="" binding="basicHttpBinding" contract="BikeStore.WcfServiceLibrary.IProductService">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8733/Design_Time_Addresses/BikeStore.WcfServiceLibrary/ProductService/" />
        </baseAddresses>
      </host>
    </service>
  </services>

Behaviours

  • <behavior> elements define sets of behaviors for the endpoints and services.
  • If a <behavior> has a name attribute, then it is assigned to endpoints or services that have their behaviorConfigution attribute set equal to the name.
  • If a <behavoir> does not have a name attribute then it contributes to the default behavior of endpoints or services. Any endpoint or service that does not has a behaviorConfiguration attribute will assume these defaults.
<system.serviceModel>
  <behaviors>
    <endpointBehaviors>
      <behavior name="myBehavior">
         <callbackDebug includeExceptionDetailInFaults="true" />
       </behavior>
    </endpointBehaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="true" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    ...
  </services>
</system.serviceModel>

Bindings

  • <binding> elements enable configuration of the endpoint bindings.
  • <binding> elements are grouped by binding type, and are associated with endpoints that have their binding attribute set equal to the binding type.
  • If a <binding> element has a name attribute then it is only associated with endpoints that have, in addition to their binding attribute set equal to the binding type, also have their bindingConfiguration attribute set equal to the name.
<system.serviceModel>
  <bindings>
    <basicHttpBinding>
      <binding name="myBindingConfiguration1" closeTimeout="00:01:00" />
      <binding name="myBindingConfiguration2" closeTimeout="00:02:00" />
      <binding closeTimeout="00:03:00" />  <!—- Default binding for basicHttpBinding -->
    </basicHttpBinding>
  </bindings>
  <services>
    ...
  </services>
</system.serviceModel>

Publishing Service Metadata

  • Add a WS-MetadataExchange (MEX) endpoint to the service
  • Add a ServiceMetadataBehavior service behavior.
<system.serviceModel>
  <services>
    <service name="BikeStore.WcfServiceLibrary.ProductService">
      ...
      <endpoint address="mex"
                binding="mexHttpBinding"
                contract="IMetadataExchange" />
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <serviceMetadata httpGetEnabled="True" policyVersion="Policy15" />
      </behavior>
    </serviceBehaviors>
  </behavoirs>
</system.serviceModel>
  • Clients can retrieve the metadata using a WS-Transfer GET request or an HTTP/GET request using the ?wsdl query string.
http://localhost:57481/ProductService.svc?wsdl
Advertisements

WCF Crib Sheet – Error Handling

SOAP Fault

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <s:Fault>
            <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher" xmlns="">a:InternalServiceFault</faultcode>
            <faultstring xml:lang="en-GB" xmlns="">The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug>configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.</faultstring>
        </s:Fault>
    </s:Body>
</s:Envelope>
  • When a .NET exception is thrown during the processing of a request, the server responds with a SOAP Fault message.
  • By default, for security reasons, the SOAP Fault message is generic and does not contain the exception details.
  • The SOAP Fault contains faultcode and a faultstring elements.

IncludeExceptionDetailsInFault

<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceDebug includeExceptionDetailInFaults="true" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <s:Fault>
            <faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher" xmlns="">a:InternalServiceFault</faultcode>
            <faultstring xml:lang="en-GB" xmlns="">Sequence contains no elements</faultstring>
            <detail xmlns="">
                <ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
                    <HelpLink i:nil="true"></HelpLink>
                    <InnerException i:nil="true"></InnerException>
                    <Message>Sequence contains no elements</Message>
                    <StackTrace>
                        ....
                    </StackTrace>
                    <Type>System.InvalidOperationException</Type>
                </ExceptionDetail>
            </detail>
        </s:Fault>
    </s:Body>
</s:Envelope>
  • To debug a service, we can configure the service to include the exception details in the SOAP fault message.
  • Set the includeExceptionDetailInFaults config setting to true.
  • In addition to the faultcode and faultstring elements, the SOAP Fault now contains a detail element.

Explicity handling exceptions with FaultException

public ProductDto Get(int productId)
{
    try
    {
        Product product = _service.GetProduct(productId);
        return Mapper.Map<Product, ProductDto>(product);
    }
    catch (Exception ex)
    {
        throw new FaultException(
            new FaultReason(ex.Message),
            new FaultCode("Exception"));
    }
}
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Body>
        <s:Fault>
            <faultcode xmlns="">s:Exception</faultcode>
            <faultstring xml:lang="en-GB" xmlns="">Sequence contains no elements</faultstring>
        </s:Fault>
    </s:Body>
</s:Envelope>
  • Error details can be returned to the client as an un-typed Fault, by throwing a new instance of System.ServiceModel.FaultException.
ProductServiceClient client = new ProductServiceClient();
ProductDto product = null;

try
{
    product = client.GetById(11);
    Console.WriteLine(product.Name);
}
catch (FaultException fex)
{
    Console.WriteLine(fex.Message);
}
finally 
{
    Console.ReadLine();
}
  • A FaultException can be caught and interrogated by the client.

Strongly Typed Faults

[DataContract]
public class ServiceFault
{
    public ServiceFault(string message)
    {
        Message = message;
    }

    [DataMember]
    public string Message { get; set; }
}

[DataContract]
public class ValidationFault : ServiceFault
{
    public ValidationFault(string message)
        : base(message)
    {
        ValidationErrors = new List<ValidationError>();
    }

    public ValidationFault(string message, List<ValidationError> validationErrors)
        : base(message)
    {
        ValidationErrors = validationErrors;
    }

    [DataMember]
    public List<ValidationError> ValidationErrors { get; set; }
}

[DataContract]
public class ValidationError
{
    public ValidationError(string property, List<string> errorMessages)
    {
        Property = property;
        ErrorMessages = errorMessages;
    }

    [DataMember]
    public string Property { get; set; }
    [DataMember]
    public List<string> ErrorMessages { get; set; }
}
  • Define one or more classes to contain the fault details.
  • The classes and properties should be decorated with the [DataContract] and [DataMember] attributes so that they are known to the client.
  • The classes can be based on System.Exception, although at the risk of revealing implementation details with the StackTrace property.
[ServiceContract(Namespace = "BikeStore.WcfServiceLibrary")]
public interface IProductService
{
    [OperationContract(Name = "GetById")]
    [FaultContract(typeof(ValidationFault))]
    ProductDto Get(int productId);
}
  • Decorate the service operation with one or more [FaultContract] attribute.
  • A [FaultContract] attribute is required for the operation to return a strongly type fault. If the attribute is omitted the service will instead return an un-typed fault.
public ProductDto Get(int productId)
{
    Product product = _service.GetProduct(productId);
    if (product == null)
    {
        throw new FaultException<ValidationFault>(new ValidationFault("Product is not valid"));
    }
    return Mapper.Map<Product, ProductDto>(product);
}
  • When a strongly typed fault exception is thrown the server will return a strongly typed fault.
  • For any other exception, and un-typed fault will be returned.
ProductServiceClient client = new ProductServiceClient();
ProductDto product = null;

try
{
    product = client.GetById(11);
    Console.WriteLine(product.Name);
}
catch (FaultException<ValidationFault> vf)
{
    Console.WriteLine(vf.Detail.Message);
}
catch (FaultException fex)
{
    Console.WriteLine(fex.Message);
}
finally
{
    Console.ReadLine();
}
  • Strongly typed faults enable the client to easily differentiate between classes of faults.
  • Strongly typed faults also enable additional error information to be sent to the client.

 

WCF Crib Sheet – Data Contract and Data Member

Data Contract Attribute

[DataContract]
public class ProductDto
{
    ...
}
  • .NET primitive types are serializable.
  • User defined types are by default not serializable.
  • The data contract attribute marks a user defined type as being serializable by the WCF data contract serializer.
  • If a user defined type is not decorated with a data contract attribute, and is a public type, then WCF will infer a data contract attribute and will serialize all members of the type.

Data Contract Inheritance

[DataContract]
public class ProductDto
{
    ...
}

[DataContract]
public class PremiumProductDto : ProductDto
{
    ...
}
  • The data contract attribute is not inheritable and so must be added to all levels in a hierarchy.

Data Member Attribute

[DataContract]
public class ProductDto
{
    [DataMember]
    public int ProductId { get; set; }

    [DataMember]
    public string Name { get; set; }
}
  • The data member attribute denotes which members of a type are serializable by the data contract serializer.
  • Can be applied on fields and properties. Best practice is to only apply to properties.
  • Properties must have both a getter and setter.
  • Can be applied on any field or property irrespective of visibility

Known Type Attribute

public class ContractService : IContractService
{
    public ContractDto GetAll()
    {
        ...
    }
}

[DataContract]
[KnownType(typeof(CreditContractDto))]
[KnownType(typeof(PrepaymentContractDto))]
public class ContractDto
{
    [DataMember]
    public int ContractId { get; set; }

    [DataMember]
    public DateTime StartDate { get; set; }

    [DataMember]
    public DateTime EndDate { get; set; }
}

[DataContract]
public class CreditContractDto : ContractDto
{
    [DataMember]
    public decimal CreditLimit { get; set; }
}

[DataContract]
public class PrepaymentContractDto : ContractDto
{
    [DataMember]
    public decimal PrepaymentAmount { get; set; }

    [DataMember]
    public PrepaymentPeriod PrepaymentPeriod { get; set; }
}
  • The known type attribute indicates the types of sub-class objects that can be expected for the data contract.

Known Type Configuration

<system.runtime.serialization>
  <dataContractSerializer>
    <declaredTypes>
      <add type="ContractDto,BikeStore.WcfServiceLibrary">
        <knownType type="CustomerContractDto,BikeStore.WcfServiceLibrary"/>
        <knownType type="PrepaymentContractDto,BikeStore.WcfServiceLibrary"/>
      </add>
    </declaredTypes>
  </dataContractSerializer>
</system.runtime.serialization>
  • An alternative to using the known type attribute is to denote known types in the service or client’s configuration file.

 

WCF Crib Sheet – Service Contract and Operation Contract

Service Contract Attribute

[ServiceContract]
public interface IProductService
{
   ...
}
  • Defines a WCF service contract.
  • Can be applied to an interface or a class. Best practice is to apply it to an interface.
  • WCF requires a class to have a parameter-less constructor.

Service Contract Names and Namespaces

[ServiceContract(Name="IBikeProductService", Namespace="BikeStore.WcfServiceLibrary")]
public interface IProductService
{     
    ...
}
  • Add a namespace to a service contract avoid potential class name collisions
  • Use the name property to change the name of the contract exposed to clients

Contract Inheritance

[ServiceContract(Namespace="BikeStore.WcfServiceLibrary")]
public interface IContractService
{
        ...
}

[ServiceContract(Namespace = "BikeStore.WcfServiceLibrary")]
public interface IPrepaymentContractService : IContractService
{
    ...
}
  • The service contract attribute is not inheritable and so must be added to all levels in a hierarchy.

Operation Contract Attribute

[ServiceContract]
public interface IProductService
{
    [OperationContract]
    ProductDto GetById(int productId);

    [OperationContract]
    IEnumerable<ProductDto> GetAll();
}
  • Indicates that a method defines an operation that is part of a service contract.
  • Methods without the attribute are not part of the service contract.
  • Can be applied to public and private methods.

Operation Overloading

[ServiceContract(Namespace="BikeStore.WcfServiceLibrary")]
public interface IProductService
{
    [OperationContract(Name="GetById")]
    ProductDto Get(int productId);

    [OperationContract(Name="GetByName")]
    ProductDto Get(string name);

    [OperationContract]
    IEnumerable<ProductDto> GetAll();
}
  • Operation names must be unique. Where the interface has overloaded methods, use the operation contract name property to alias the operation.