Chapter 43

Windows Communication Foundation

WHAT’S IN THIS CHAPTER?

  • WCF overview
  • Creating a simple service and client
  • Defining service, operation, data, and message contracts
  • Implementing a service
  • Using binding for communication
  • Creating different hosts for services
  • Creating clients with a service reference and programmatically
  • Using duplex communication
  • Using routing

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

You can find the wrox.com code downloads for this chapter at http://www.wrox.com/remtitle.cgi?isbn=1118314425 on the Download Code tab. The code for this chapter is divided into the following major examples:

  • Simple service and client
  • WebSocket
  • Duplex communication
  • Routing

WCF OVERVIEW

Windows Communication Foundation (WCF) is the flexible communication technology of the .NET Framework. Previous to .NET 3.0, several communication technologies were required in a single enterprise solution. For platform-independent communication, ASP.NET Web services were used. For more advanced web services — technologies such as reliability, platform-independent security, and atomic transactions — Web Services Enhancements added a complexity layer to ASP.NET web services. If the communication needed to be faster, and both the client and service were .NET applications, .NET Remoting was the technology of choice. .NET Enterprise Services with its automatic transaction support, by default, used the DCOM protocol, which was even faster than .NET Remoting. DCOM was also the only protocol to allow the passing of transactions. All of these technologies have different programming models that require many skills from the developer.

.NET Framework 3.0 introduced a new communication technology that includes all the features from these predecessors and combines them into one programming model: Windows Communication Foundation (WCF).

The namespace covered in this chapter is System.ServiceModel.

WCF combines the functionality from ASP.NET Web services, .NET Remoting, Message Queuing, and Enterprise Services. You can get the following from WCF:

  • Hosting for components and services — Just as you can use custom hosts with .NET Remoting and Web Service Enhancements (WSE), you can host a WCF service in the ASP.NET runtime, a Windows service, a COM+ process, or just a Windows Forms application for peer-to-peer computing.
  • Declarative behavior — Instead of the requirement to derive from a base class (this requirement exists with .NET Remoting and Enterprise Services), attributes can be used to define the services. This is similar to web services developed with ASP.NET.
  • Communication channels — Although .NET Remoting is flexible for changing the communication channel, WCF is a good alternative because it offers the same flexibility. WCF offers multiple channels to communicate using HTTP, TCP, or an IPC channel. Custom channels using different transport protocols can be created as well.
  • Security infrastructure — For implementing platform-independent web services, a standardized security environment must be used. The proposed standards are implemented with WSE 3.0, and this continues with WCF.
  • Extensibility — .NET Remoting has a rich extensibility story. It is not only possible to create custom channels, formatters, and proxies, but also to inject functionality inside the message flow on the client and on the server. WCF offers similar extensibilities; however, here the extensions are created by using SOAP headers.
  • Support of previous technologies — Instead of rewriting a distributed solution completely to use WCF, WCF can be integrated with existing technologies. WCF offers a channel that can communicate with serviced components using DCOM. Web services that have been developed with ASP.NET can be integrated with WCF as well.

The final goal is to send and receive messages between a client and a service across processes or different systems, across a local network, or across the Internet. This should be done, if required, in a platform-independent way and as fast as possible. From a distant view, the service offers an endpoint that is described by a contract, a binding, and an address. The contract defines the operations offered by the service; binding gives information about the protocol and encoding; and the address is the location of the service. The client needs a compatible endpoint to access the service.

Figure 43-1 shows the components that participate with a WCF communication.

The client invokes a method on the proxy. The proxy offers methods as defined by the service but converts the method call to a message and transfers the message to the channel. The channel has a client-side part and a server-side part that communicate across a networking protocol. From the channel, the message is passed to the dispatcher, which converts the message to a method call invoked with the service.

WCF supports several communication protocols. For platform-independent communication, web services standards are supported. For communication between .NET applications, faster communication protocols with less overhead can be used.

The following sections look at the functionality of core services used for platform-independent communication:

  • SOAP — A platform-independent protocol that is the foundation of several web service specifications to support security, transactions, reliability
  • Web Services Description Language (WSDL) — Offers metadata to describe a service
  • Representational State Transfer (REST) — Used with RESTful Web services to communicate across HTTP
  • JavaScript Object Notation (JSON) — Enables easy use from within JavaScript clients

SOAP

For platform-independent communication, the SOAP protocol can be used and is directly supported from WCF. SOAP originally was shorthand for Simple Object Access Protocol (SOAP), but since SOAP 1.2 this is no longer the case. SOAP no longer is an object access protocol because instead messages are sent that can be defined by an XML schema.

A service receives a SOAP message from a client and returns a SOAP response message. A SOAP message consists of an envelope, which contains a header and a body:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
  </s:Header>
  <s:Body>
    <ReserveRoom xmlns="http://www.cninnovation.com/RoomReservation/2012">
      <roomReservation
          xmlns:a=
            "http://schemas.datacontract.org/2004/07/Wrox.ProCSharp.WCF.Contracts"
          xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:Contact>UEFA</a:Contact>
        <a:EndTime>2012-07-01T22:45:00</a:EndTime>
        <a:Id>0</a:Id>
        <a:RoomName>Kiew</d4p1:RoomName>
        <a:StartTime>2012–07–01T20:45:00</a:StartTime>
        <a:Text>Spain-Germany</a:Text>
      </roomReservation>
    </ReserveRoom>
  </s:Body>
</s:Envelope>

The header is optional and can contain information about addressing, security, and transactions. The body contains the message data.

WSDL

A Web Services Description Language (WSDL) document describes the operations and messages of the service. WSDL defines metadata of the service that can be used to create a proxy for the client application.

The WSDL contains this information:

  • Types for the messages described using an XML schema.
  • Messages sent to and from the service. Parts of the messages are the types defined with an XML schema.
  • Port types map to service contracts and list operations defined with the service contract. Operations contain messages; for example, an input and an output message as used with a request and response sequence.
  • Binding information that contains the operations listed with the port types and that defines the SOAP variant used.
  • Service information that maps port types to endpoint addresses.

NOTE With WCF, WSDL information is offered by Metadata Exchange (MEX) endpoints.

REST

WCF also offers communication by using REST. This is not actually a protocol but defines several principles for using services to access resources. A RESTful Web service is a simple service based on the HTTP protocol and REST principles. The principles are defined by three categories: a service can be accessed with a simple URI, supports MIME types, and uses different HTTP methods. With the support of MIME types, different data formats can be returned from the service such as plain XML, JSON, or AtomPub. The GET method of a HTTP request returns data from the service. Other methods that are used are PUT, POST, and DELETE. The PUT method is used to make an update on the service side, POST creates a new resource, and DELETE deletes a resource.

REST enables the sending of smaller requests to services than is possible with SOAP. If transactions, secure messages, (secure communication is still possible, for example via HTTPS), and the reliability offered by SOAP are not needed, a REST-architected service can reduce overhead.

With the REST architecture, the service is always stateless, and the response from the service can be cached.

JSON

Instead of sending SOAP messages, accessing services from JavaScript can best be done by using JSON. .NET includes a data contract serializer to create objects with the JSON notation.

JSON has less overhead than SOAP because it is not XML but is optimized for JavaScript clients. This makes it extremely useful from Ajax clients. Ajax is discussed in Chapter 40, “ASP.NET Web Forms.” JSON does not provide the reliability, security, and transaction features that can be sent with the SOAP header, but these are features usually not needed by JavaScript clients.

CREATING A SIMPLE SERVICE AND CLIENT

Before going into the details of WCF, start with a simple service. The service is used to reserve meeting rooms.

For a backing store of room reservations, a simple SQL Server database with the table RoomReservations is used. You can download the database from www.wrox.com together with the sample code of this chapter.

Following are the next steps to create a service and a client:

1. Create service and data contracts.
2. Create a library to access the database using the ADO.NET Entity Framework.
3. Implement the service.
4. Use the WCF Service Host and WCF Test Client.
5. Create a custom service host.
6. Create a client application using metadata.
7. Create a client application using shared contracts.
8. Configure diagnostics.

Defining Service and Data Contracts

To start, create a new solution with the name RoomReservation. Add a new project of type Class Library to the solution, and name the project RoomReservationContracts. Create a new class named RoomReservation (code file RoomReservation/RoomReservationContracts/RoomReservation.cs). This class contains the properties Id, RoomName, StartTime, EndTime, Contact, and Text to define the data needed in the database and sent across the network. For sending the data across a WCF service, the class is annotated with the DataContract and the DataMember attributes. The attributes StringLength from the namespace System.ComponentModel.DataAnnotations can not only be used with validation on user input, but they can also define column schemas on creating the database table.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
 
namespace Wrox.ProCSharp.WCF.Contracts
{
  [DataContract]
  public class RoomReservation : INotifyPropertyChanged
  {
    private int id;
 
    [DataMember]
    public int Id
    {
      get { return id; }
      set { SetProperty(ref id, value); }
    }
 
    private string roomName;
 
    [DataMember]
    [StringLength(30)]
    public string RoomName
    {
      get { return roomName; }
      set { SetProperty(ref roomName, value); }
    }
 
    private DateTime startTime;
 
    [DataMember]
    public DateTime StartTime
    {
      get { return startTime; }
      set { SetProperty(ref startTime, value); }
    }
 
    private DateTime endTime;
 
    [DataMember]
    public DateTime EndTime
    {
      get { return endTime; }
      set { SetProperty(ref endTime, value); }
    }
 
    private string contact;
 
    [DataMember]
    [StringLength(30)]
    public string Contact
    {
      get { return contact; }
      set { SetProperty(ref contact, value); }
    }
 
    private string text;
 
    [DataMember]
    [StringLength(50)]
    public string Text
    {
      get { return text; }
      set { SetProperty(ref text, value); }
    } 
 
    protected virtual void OnNotifyPropertyChanged(string propertyName)
    {
      PropertyChangedEventHandler eventHandler = PropertyChanged;
      if (eventHandler != null)
      {
        eventHandler(this, new PropertyChangedEventArgs(propertyName));
      }
    }
 
    protected virtual void SetProperty<T>(ref T item, T value, 
      [CallerMemberName] string propertyName = null)
    {
      if (!EqualityComparer<T>.Default.Equals(item, value))
      {
        item = value;
        OnNotifyPropertyChanged(propertyName);
      }
    }
 
    public event PropertyChangedEventHandler PropertyChanged;
  }
}

Next, create the service contract. The operations offered by the service can be defined by an interface. The interface IRoomService defines the methods ReserveRoom and GetRoomReservations. The service contract is defined with the attribute ServiceContract. The operations defined by the service have the attribute OperationContract applied (code file RoomReservation/RoomReservationContracts/IRoomService.cs).

using System;
using System.ServiceModel;
        
namespace Wrox.ProCSharp.WCF.Contracts
{
  [ServiceContract(Namespace= "http://www.cninnovation.com/RoomReservation/2012")]
  public interface IRoomService
  {
    [OperationContract]
    bool ReserveRoom(RoomReservation roomReservation);
        
    [OperationContract]
    RoomReservation[] GetRoomReservations(DateTime fromTime, DateTime toTime);
  }
}

Data Access

Next, create a library used to access, read and write reservations to the database named RoomReservationData. This time use the Code First model with the ADO.NET Entity Framework. This way mapping information is not needed; everything can be defined using code. And you can also create a database on-the-fly during runtime. The class to define the entities was already defined with the RoomReservationContracts assembly, so this assembly needs to be referenced. Also the EntityFramework assembly is required. Now the RoomReservationContext class (code file RoomReservation/RoomReservationData/RoomReservationContext.cs) can be created. This class derives from the base class DbContext to act as a context for the ADO.NET Entity Framework and defines a property named RoomReservations to return a DbSet<RoomReservation>.

using System.Data.Entity;
using Wrox.ProCSharp.WCF.Contracts;
 
namespace Wrox.ProCSharp.WCF.Data
{
  public class RoomReservationContext : DbContext
  {
 
    public RoomReservationContext()
      : base("name=RoomReservation")
    {
 
    }
    public DbSet<RoomReservation> RoomReservations { get; set; }
  }
}

With the default constructor of the class, the base constructor is invoked to pass the name of an SQL connection string. This way on creation of the database a name is not automatically mapped from the name of the context. If the database does not exist before starting the application, it is automatically created on first use of the context. The hosting application than needs a connection string is configured as shown. The connection string makes use of the Microsoft SQL Server Express LocalDB database that is a new improved version of SQL Express and comes with the installation of Visual Studio 2012.

<connectionStrings>
  <add
    name="RoomReservation" providerName="System.Data.SqlClient"
    connectionString="Server=(localdb)v11.0;Database=RoomReservation;
    Trusted_Connection=true;Integrated Security=True;
    MultipleActiveResultSets=True"/>
</connectionStrings>

Functionality that will be used by the service implementation is defined with the RoomReservationData class (code file RoomReservation/RoomReservationData/RoomReservationData.cs). The method ReserveRoom writes a new record to the database, and the method GetReservations returns a collection of RoomReservation for a specified time span.

using System;
using System.Linq;
using Wrox.ProCSharp.WCF.Contracts;
 
namespace Wrox.ProCSharp.WCF.Data
{
  public class RoomReservationData
  {
    public void ReserveRoom(RoomReservation roomReservation)
    {
      using (var data = new RoomReservationContext())
      {
        data.RoomReservations.Add(roomReservation);
        data.SaveChanges();
      }
    }
 
    public RoomReservation[] GetReservations(DateTime fromTime, DateTime toTime)
    {
      using (var data = new RoomReservationContext())
      {
        return (from r in data.RoomReservations
                where r.StartTime > fromTime && r.EndTime < toTime
                select r).ToArray();
      }
    }
  }
}

NOTE Chapter 33, “ADO.NET Entity Framework,” gives you the details of the ADO.NET Entity Framework.

Service Implementation

Now you can step into the implementation of the service. Create a WCF service library named RoomReservationService. By default, this library type contains both the service contract and the service implementation. If the client application just uses metadata information to create a proxy accessing the service, this model is okay to work with. However, if the client might use the contract types directly, it is a better idea to put the contracts in a separate assembly as it was done here. With the first client that is done, a proxy is created from metadata. Later you can see how to create a client to share the contract assembly. Splitting the contracts and implementation is a good preparation for this.

The service class RoomReservationService implements the interface IRoomService. The service is implemented just by invoking the appropriate methods of the RoomReservationData class (code file RoomReservation/RoomReservationService/RoomReservationService.cs):

using System;
using System.ServiceModel;
using Wrox.ProCSharp.WCF.Contracts;
using Wrox.ProCSharp.WCF.Data;
        
namespace Wrox.ProCSharp.WCF.Service
{
  [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
  public class RoomReservationService : IRoomService
  {
    public bool ReserveRoom(RoomReservation roomReservation)
    {
      var data = new RoomReservationData();
      data.ReserveRoom(roomReservation);
      return true;
    }
        
    public RoomReservation[] GetRoomReservations(DateTime fromTime, 
        DateTime toTime)
    {
      var data = new RoomReservationData();
      return data.GetReservations(fromTime, toTime);
    }
  }
}

Figure 43-2 shows the assemblies created so far and their dependencies. The RoomReservationContracts assembly is used by both RoomReservationData and RoomReservationService.

WCF Service Host and WCF Test Client

The WCF Service Library project template creates an application configuration file named App.config that you need to adapt to the new class and interface names. The service element references the service type RoomReservationService, including the namespace; the contract interface needs to be defined with the endpoint element (configuration file RoomReservation/RoomReservationService/app.config):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.web>
    <compilation debug="true" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="Wrox.ProCSharp.WCF.Service.RoomService">
        <endpoint address="" binding="basicHttpBinding" 
            contract="Wrox.ProCSharp.WCF.Service.IRoomService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" 
            contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress=
"http://localhost:8733/Design_Time_Addresses/RoomReservationService/Service1/" 
            />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

NOTE The service address http://localhost:8733/Design_Time_Addresses has an access control list (ACL) associated with it that enables the interactive user to create a listener port. By default, a nonadministrative user is not allowed to open ports in listening mode. You can view the ACLs with the command-line utility netsh http show urlacl and add new entries with netsh http add urlacl url=http://+:8080/MyURI user=someUser.

Starting this library from Visual Studio 2012 starts the WCF Service Host, which appears as an icon in the notification area of the taskbar. Clicking this icon opens the WCF Service Host window (see Figure 43-3), where you can see the status of the service. The project properties of a WCF library application include the tab WCF options, where you can select whether the WCF service host should be started when running a project from the same solution. By default, this option is turned on. Also, with the Debug configuration of the project properties, you can find the command-line argument /client: "WcfTestClient.exe" defined. With this option, the WCF Service host starts the WCF Test Client (see Figure 43-4), which you can use to test the application. When you double-click an operation, input fields appear on the right side of the application that you can fill to send data to the service. When you click the XML tab, you can see the SOAP messages that have been sent and received.

Custom Service Host

WCF enables services to run in any host. You can create a Windows Forms or Windows Presentation Foundation (WPF) application for peer-to-peer services. Or you can create a Windows service or host the service with Windows Activation Services (WAS) or Internet Information Services (IIS). A console application is also good to demonstrate a simple custom host.

With the service host, you must reference the library RoomReservationService in addition to the assembly System.ServiceModel. The service is started by instantiating and opening an object of type ServiceHost. This class is defined in the namespace System.ServiceModel. The RoomReservationService class that implements the service is defined in the constructor. Invoking the Open()method starts the listener channel of the service — the service is ready to listen for requests. The Close()method stops the channel. The code snippet also adds a behavior of type ServiceMetadataBehavior. This behavior is added to allow creating a client application by using WSDL (code file RoomReservation/RoomReservationServiceHost/Program.cs).

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using Wrox.ProCSharp.WCF.Service;
 
namespace Wrox.ProCSharp.WCF.Host
{
  class Program
  {
    internal static ServiceHost myServiceHost = null;
        
    internal static void StartService()
    {
      try
      {
        myServiceHost = new ServiceHost(typeof(RoomReservationService), 
          new Uri("http://localhost:9000/RoomReservation"));
        myServiceHost.Description.Behaviors.Add(new ServiceMetadataBehavior 
        { HttpGetEnabled = true });
        myServiceHost.Open();
      }
      catch (AddressAccessDeniedException)
      {
        Console.WriteLine("either start Visual Studio in elevated admin " + 
            "mode or register the listener port with netsh.exe");
      }
    }
        
    internal static void StopService()
    {
      if (myServiceHost != null && 
          myServiceHost.State == CommunicationState.Opened)
      {
        myServiceHost.Close();
      }
    }
        
    static void Main()
    {
      StartService();
        
      Console.WriteLine("Server is running. Press return to exit");
      Console.ReadLine();
       
      StopService();
    }
  }
}

For the WCF configuration, you can copy the application configuration file created with the service library to the host application. You can edit this configuration file with the WCF Service Configuration Editor (see Figure 43-5).

Instead of using the configuration file, you can configure everything programmatically and also use several defaults. The sample code for the host application doesn’t need any configuration file. The second parameter of the ServiceHost constructor defines a base address for the service. With the protocol of this base address, a default binding is defined. The default for the HTTP is the BasicHttpBinding.

Using the custom service host, you can deselect the WCF option to start the WCF Service Host in the project settings of the WCF library.

WCF Client

For the client, WCF is flexible again in what application type can be used. The client can be a simple console application as well. However, for reserving rooms, create a simple WPF application with controls, as shown in Figure 43-6.

Because the service host is configured with the ServiceMetadataBehavior, it offers a MEX endpoint. After the service host is started, you can add a service reference from Visual Studio. Adding the service reference, the dialog shown in Figure 43-7 pops up. Enter the link to the service metadata with the URL http://localhost:9000/RoomReservation?wsdl, and set the namespace name to RoomReservationService. This defines the namespace of the generated proxy class.

Adding a service reference adds references to the assemblies System.Runtime.Serialization and System.ServiceModel and a configuration file containing the binding information and the endpoint address to the service.

From the data contract the class RoomReservation is generated as a partial class. This class contains all [DataMember] elements of the contract. The class RoomServiceClient is the proxy for the client that contains methods that are defined by the operation contracts. Using this client, you can send a room reservation to the running service.

In the code file RoomReservation/RoomReservationClient/MainWindow.xaml.cs, the OnReserveRoom method is invoked with the Click event of the button. The ReserveRoomAsync is invoked with the service proxy. The reservation variable receives the data from the UI via data binding.

  public partial class MainWindow : Window
  {
    private RoomReservation reservation;
    public MainWindow()
    {
      InitializeComponent();
      reservation = new RoomReservation 
      { StartTime = DateTime.Now, EndTime = DateTime.Now.AddHours(1) };
      this.DataContext = reservation;
    }
 
    private async void OnReserveRoom(object sender, RoutedEventArgs e)
    {
      var client = new RoomServiceClient();
      bool reserved = await client.ReserveRoomAsync(reservation);
      client.Close();
 
      if (reserved)
        MessageBox.Show("reservation ok");
    }
  }

By running both the service and the client, the database is created, and you can add room reservations to the database. With the settings of the RoomReservation solution, you can configure multiple startup projects, which should be RoomReservationClient and RoomReservationHost in this case.

Diagnostics

When running a client and service application, it can be helpful to know what’s happening behind the scenes. For this, WCF makes use of a trace source that just needs to be configured. You can configure tracing using the Service Configuration Editor, selecting Diagnostics, and enabling Tracing and Message Logging. Setting the trace level of the trace sources to Verbose produces detailed information. This configuration change adds trace sources and listeners to the application configuration file as shown here:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add
      name="RoomReservation" providerName="System.Data.SqlClient"
      connectionString="Server=(localdb)v11.0;Database=RoomReservation;    
      Trusted_Connection=true;Integrated Security=True;
      MultipleActiveResultSets=True" />
  </connectionStrings>
  
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging" 
        switchValue="Verbose,ActivityTracing">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type="" />
          </add>
          <add name="ServiceModelMessageLoggingListener">
            <filter type="" />
          </add>
        </listeners>
      </source>
      <source propagateActivity="true" name="System.ServiceModel" 
        switchValue="Warning,ActivityTracing">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type="" />
          </add>
          <add name="ServiceModelTraceListener">
            <filter type="" />
          </add>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add initializeData=
        "c:codewcf
oomreservation
oomreservationhostapp_messages.svclog"
        type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0,   
        Culture=neutral, PublicKeyToken=b77a5c561934e089"
        name="ServiceModelMessageLoggingListener" 
        traceOutputOptions="DateTime, Timestamp, ProcessId, ThreadId">
        <filter type="" />
      </add>
      <add initializeData=
        "c:codewcf
oomreservation
oomreservationhostapp_tracelog.svclog"
        type="System.Diagnostics.XmlWriterTraceListener, System, Version=4.0.0.0, 
        Culture=neutral, PublicKeyToken=b77a5c561934e089"
        name="ServiceModelTraceListener" 
        traceOutputOptions="DateTime, Timestamp, ProcessId, ThreadId">
        <filter type="" />
      </add>
    </sharedListeners>
  </system.diagnostics>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
 
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true" logMalformedMessages="true"
        logMessagesAtTransportLevel="true" />
      <endToEndTracing propagateActivity="true" activityTracing="true"
        messageFlowTracing="true" />
    </diagnostics>
  </system.serviceModel>
</configuration>

NOTE The implementation of the WCF classes uses the trace sources named System.ServiceModel and System.ServiceModel.MessageLogging for writing trace messages. You can read more about tracing and configuring trace sources and listeners in Chapter 20, “Diagnostics.”

When you start the application, the trace files soon get large with verbose trace settings. To analyze the information from the XML log file, the .NET SDK includes the Service Trace Viewer tool, svctraceviewer.exe. Figure 43-8 shows the client application with some data entered, and Figure 43-9 shows the view from the svctraceviewer.exe after selecting the trace and message log files. The BasicHttpBinding is light with the messages sent across. If you change the configuration to use the WsHttpBinding, you see many messages related to security. Depending on your security needs, you can choose other configuration options.

The following sections discuss the details and different options of WCF.

Sharing Contract Assemblies with the Client

With the previous WPF client application, a proxy class was created using the metadata, adding a service reference with Visual Studio. A client can also be created by using the shared contract assembly as is shown now. Using the contract interface, the ChannelFactory<TChannel> class is used to instantiate the channel to connect to the service.

The constructor of the class ChannelFactory<TChannel> accepts the binding configuration and endpoint address. The binding must be compatible with the binding defined with the service host, and the address defined with the EndpointAddress class references the URI of the running service. The CreateChannel method creates a channel to connect to the service. Then, you can invoke methods of the service.

using System;
using System.ServiceModel;
using System.Windows;
using Wrox.ProCSharp.WCF.Contracts;
 
namespace RoomReservationClientSharedAssembly
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    private RoomReservation roomReservation;
    public MainWindow()
    {
      InitializeComponent();
      roomReservation = new RoomReservation
      {
        StartTime = DateTime.Now,
        EndTime = DateTime.Now.AddHours(1)
      };
      this.DataContext = roomReservation;
    }
 
    private void OnReserveRoom(object sender, RoutedEventArgs e)
    {
      var binding = new BasicHttpBinding();
      var address = new EndpointAddress("http://localhost:9000/RoomReservation");
 
      var factory = new ChannelFactory<IRoomService>(binding, address);
      IRoomService channel = factory.CreateChannel();
      if (channel.ReserveRoom(roomReservation))
      {
        MessageBox.Show("success");
      }
    }
  }
}

CONTRACTS

A contract defines what functionality a service offers and what functionality can be used by the client. The contract can be completely independent of the implementation of the service.

The contracts defined by WCF can be grouped into four different contract types: Data, Service, Message, and Fault. The contracts can be specified by using .NET attributes:

  • Data contract — The data contract defines the data received by and returned from the service. The classes used for sending and receiving messages have data contract attributes associated with them.
  • Service contract — The service contract is used to define the WSDL that describes the service. This contract is defined with interfaces or classes.
  • Operation contract — The operation contract defines the operation of the service and is defined within the service contract.
  • Message contract — If complete control over the SOAP message is needed, a message contract can specify what data should go into the SOAP header and what belongs in the SOAP body.
  • Fault contract — The fault contract defines the error messages that are sent to the client.

The following sections explore these contract types further and discuss versioning issues that should be thought about when defining the contracts.

Data Contract

With the data contract, CLR types are mapped to XML schemas. The data contract is different from other .NET serialization mechanisms: with runtime serialization, all fields are serialized (including private fields); with XML serialization, only the public fields and properties are serialized. The data contract requires explicit marking of the fields that should be serialized with the DataMember attribute. This attribute can be used regardless of whether the field is private or public, or if it is applied to a property.

[DataContract(Namespace="http://www.cninnovation.com/Services/20102"]
public class RoomReservation
{
   [DataMember] public string Room { get; set; }
   [DataMember] public DateTime StartTime { get; set; }
   [DataMember] public DateTime EndTime { get; set; }
   [DataMember] public string Contact { get; set; }
   [DataMember] public string Text { get; set; }
}

To be platform-independent, and provide the option to change data with new versions without breaking older clients and services, using data contracts is the best way to define which data should be sent. However, you can also use XML serialization and runtime serialization. XML serialization is the mechanism used by ASP.NET Web services; .NET Remoting uses runtime serialization.

With the attribute DataMember, you can specify the properties described in the following table.

DATAMEMBER PROPERTY DESCRIPTION
Name By default, the serialized element has the same name as the field or property where the [DataMember] attribute is applied. You can change the name with the Name property.
Order The Order property defines the serialization order of the data members.
IsRequired With the IsRequired property, you can specify that the element must be received with serialization. This property can be used for versioning. If you add members to an existing contract, the contract is not broken because, by default, the fields are optional (IsRequired=false). You can break an existing contract by setting IsRequired to true.
EmitDefaultValue The property EmitDefaultValue defines whether the member should be serialized if it has the default value. If EmitDefaultValue is set to true, the member is not serialized if it has the default value for the type.

Versioning

When you create a new version of a data contract, pay attention to what kind of change it is and act accordingly if old and new clients and old and new services should be supported simultaneously.

When defining a contract, you should add XML namespace information with the Namespace property of the DataContractAttribute. This namespace should be changed if a new version of the data contract is created that breaks compatibility. If just optional members are added, the contract is not broken — this is a compatible change. Old clients can still send a message to the new service because the additional data is not needed. New clients can send messages to an old service because the old service just ignores the additional data.

Removing fields or adding required fields breaks the contract. Here, you should also change the XML namespace. The name of the namespace can include the year and the month, for example, http://www.cninnovation.com/Services/2012/08. Every time a breaking change is done, the namespace is changed; for example, by changing the year and month to the actual value.

Service and Operation Contracts

The service contract defines the operations the service can perform. The attribute ServiceContract is used with interfaces or classes to define a service contract. The methods that are offered by the service have the attribute OperationContract applied, as you can see with the interface IRoomService:

[ServiceContract]
public interface IRoomService
{
  [OperationContract]
  bool ReserveRoom(RoomReservation roomReservation);
}

The possible properties that you can set with the ServiceContract attribute are described in the following table.

SERVICECONTRACT PROPERTY DESCRIPTION
ConfigurationName This property defines the name of the service configuration in a configuration file.
CallbackContract When the service is used for duplex messaging, the property CallbackContract defines the contract that is implemented in the client.
Name The Name property defines the name for the <portType> element in the WSDL.
Namespace The Namespace property defines the XML namespace for the <portType> element in the WSDL.
SessionMode With the SessionMode property, you can define whether sessions are required for calling operations of this contract. The possible values Allowed, NotAllowed, and Required are defined with the SessionMode enumeration.
ProtectionLevel The ProtectionLevel property defines whether the binding must support protecting the communication. Possible values defined by the ProtectionLevel enumeration are None, Sign, and EncryptAndSign.

With the OperationContract, you can specify properties, as shown in the following table.

OPERATIONCONTRACT PROPERTY DESCRIPTION
Action WCF uses the Action of the SOAP request to map it to the appropriate method. The default value for the Action is a combination of the contract XML namespace, the name of the contract, and the name of the operation. If the message is a response message, Response is added to the Action string. You can override the Action value by specifying the Action property. If you assign the value “*”, the service operation handles all messages.
ReplyAction Whereas Action sets the Action name of the incoming SOAP request, ReplyAction sets the Action name of the reply message.
AsyncPattern If the operation is implemented by using an asynchronous pattern, set the AsyncPattern property to true. The async pattern is discussed in Chapter 21, “Threads, Tasks, and Synchronization.”
IsInitiating IsTerminating If the contract consists of a sequence of operations, the initiating operation should have the IsInitiating property assigned to it; the last operation of the sequence needs the IsTerminating property assigned. The initiating operation starts a new session; the server closes the session with the terminating operation.
IsOneWay With the IsOneWay property set, the client does not wait for a reply message. Callers of a one-way operation have no direct way to detect a failure after sending the request message.
Name The default name of the operation is the name of the method the operation contract is assigned to. You can change the name of the operation by applying the Name property.
ProtectionLevel With the ProtectionLevel property, you define whether the message should be signed or encrypted and signed.

With the service contract, you can also define the requirements that the service has from the transport with the attribute [DeliveryRequirements]. The property RequireOrderedDelivery defines that the messages sent must arrive in the same order. With the property QueuedDeliveryRequirements, you can define that the message should be sent in a disconnected mode, for example, by using Message Queuing (covered in Chapter 47, “Message Queuing”).

Message Contract

A message contract is used if complete control over the SOAP message is needed. With the message contract, you can specify what part of the message should go into the SOAP header and what belongs in the SOAP body. The following example shows a message contract for the class ProcessPersonRequestMessage. The message contract is specified with the attribute MessageContract. The header and body of the SOAP message are specified with the attributes MessageHeader and MessageBodyMember. By specifying the Position property, you can define the element order within the body. You can also specify the protection level for header and body fields.

[MessageContract]
public class ProcessPersonRequestMessage
{
  [MessageHeader]
  public int employeeId;
        
  [MessageBodyMember(Position=0)]
  public Person person;
}

The class ProcessPersonRequestMessage is used with the service contract defined with the interface IProcessPerson:

[ServiceContract]
public interface IProcessPerson
{
  [OperationContract]
  public PersonResponseMessage ProcessPerson(ProcessPersonRequestMessage message);
}

Another contract that is important for WCF services is the fault contract. This contract is discussed in the next section with Error Handling.

Fault Contract

By default, the detailed exception information that occurs in the service is not returned to the client application. The reason for this behavior is security. You wouldn’t want to give detailed exception information to a third party by using your service. Instead, the exception should be logged on the service (which you can do with tracing and event logging), and an error with useful information should be returned to the caller.

You can return SOAP faults by throwing a FaultException. Throwing a FaultException creates an untyped SOAP fault. The preferred way to return errors is to generate a strongly typed SOAP fault.

The information that should be passed with a strongly typed SOAP fault is defined with a data contract, as shown with the RoomReservationFault class (code file RoomReservation/RoomReservationContracts/RoomReservationFault.cs):

  [DataContract]
  public class RoomReservationFault
  {
    [DataMember]
    public string Message { get; set; }
  }

The type of the SOAP fault must be defined by using the FaultContractAttribute with the operation contract:

    [FaultContract(typeof(RoomReservationFault))]
    [OperationContract]
    bool ReserveRoom(RoomReservation roomReservation);

With the implementation, a FaultException<TDetail> is thrown. With the constructor, you can assign a new TDetail object, which is a StateFault in the example. In addition, error information within a FaultReason can be assigned to the constructor. FaultReason supports error information in multiple languages.

      FaultReasonText[] text = new FaultReasonText[2];
      text[0] = new FaultReasonText("Sample Error", new CultureInfo("en"));
      text[1] = new FaultReasonText("Beispiel Fehler", new CultureInfo("de"));
      FaultReason reason = new FaultReason(text);
        
      throw new FaultException<RoomReservationFault>(
        new RoomReservationFault() { Message = m }, reason);

With the client application, exceptions of type FaultException<RoomReservationFault> can be caught. The reason for the exception is defined by the Message property; the RoomReservationFault is accessed with the Detail property:

      try
      {
        //...
      }
      catch (FaultException<RoomReservationFault> ex)
      {
        Console.WriteLine(ex.Message);
        StateFault detail = ex.Detail;
        Console.WriteLine(detail.Message);
      }

In addition to catching the strongly typed SOAP faults, the client application can also catch exceptions of the base class of FaultException<Detail>: FaultException and CommunicationException. By catching CommunicationException, you can also catch other exceptions related to the WCF communication.


NOTE During development you can return exceptions to the client. To enable exceptions propagated, you need to configure a service behavior configuration with the serviceDebug element. The serviceDebug element has the attribute IncludeExceptionDetailInFaults that can be set to true to return exception information.

SERVICE BEHAVIORS

The implementation of the service can be marked with the attribute ServiceBehavior, as shown with the class RoomReservationService:

  [ServiceBehavior]
  public class RoomReservationService: IRoomService
  {
    public bool ReserveRoom(RoomReservation roomReservation)
    {
      // implementation
    }
  }

The attribute ServiceBehavior is used to describe behavior as is offered by WCF services to intercept the code for required functionality, as shown in the following table.

SERVICEBEHAVIOR PROPERTY DESCRIPTION
TransactionAutoComplete OnSessionClose When the current session is finished without error, the transaction is automatically committed. This is similar to the AutoComplete attribute used with Enterprise Services.
TransactionIsolationLevel To define the isolation level of the transaction within the service, the property TransactionIsolationLevel can be set to one value of the IsolationLevel enumeration. You can read information about transaction information levels in Chapter 25, “Transactions.”
ReleaseServiceInstanceOn TransactionComplete When the transaction finishes, the instance of the service recycles.
AutomaticSessionShutdown If the session should not be closed when the client closes the connection, you can set the property AutomaticSessionShutdown to false. By default, the session is closed.
InstanceContextMode With the property InstanceContextMode, you can define whether stateful or stateless objects should be used. The default setting is InstanceContextMode.PerCall to create a new object with every method call. Other possible settings are PerSession and Single. With both of these settings, stateful objects are used. However, with PerSession a new object is created for every client. Single enables the same object to be shared with multiple clients.
ConcurrencyMode Because stateful objects can be used by multiple clients (or multiple threads of a single client), you must pay attention to concurrency issues with such object types. If the property ConcurrencyMode is set to Multiple, multiple threads can access the object, and you must deal with synchronization. If you set the option to Single, only one thread accesses the object at a time. Here, you don’t have to do synchronization; however, scalability problems can occur with a higher number of clients. The value Reentrant means that only a thread coming back from a callout might access the object. For stateless objects, this setting has no meaning because new objects are instantiated with every method call and thus no state is shared.
UseSynchronizationContext With Windows Forms and WPF, members of controls can be invoked only from the creator thread. If the service is hosted in a Windows application, and the service methods invoke control members, set the UseSynchronizationContext to true. This way, the service runs in a thread defined by the SynchronizationContext.
IncludeExceptionDetailInFaults With .NET, errors show up as exceptions. SOAP defines that a SOAP fault is returned to the client in case the server has a problem. For security reasons, it’s not a good idea to return details of server-side exceptions to the client. Thus, by default, exceptions are converted to unknown faults. To return specific faults, throw an exception of type FaultException. For debugging purposes, it can be helpful to return the real exception information. This is the case when changing the setting of IncludeExceptionDetailIn Faults to true. Here a FaultException<TDetail> is thrown where the original exception contains the detail information.
MaxItemsInObjectGraph With the property MaxItemsInObjectGraph, you can limit the number of objects that are serialized. The default limitation might be too low if you serialize a tree of objects.
ValidateMustUnderstand The property ValidateMustUnderstand set to true means that the SOAP headers must be understood (which is the default).

To demonstrate a service behavior, the interface IStateService defines a service contract with two operations to set and get state. With a stateful service contract, a session is needed. That’s why the SessionMode property of the service contract is set to SessionMode.Required. The service contract also defines methods to initiate and close the session by applying the IsInitiating and IsTerminating properties to the operation contract:

  [ServiceContract(SessionMode=SessionMode.Required)]
  public interface IStateService
  {
    [OperationContract(IsInitiating=true)]
    void Init(int i);
        
    [OperationContract]
    void SetState(int i);
        
    [OperationContract]
    int GetState();
        
 
    [OperationContract(IsTerminating=true)]
    void Close();
  }

The service contract is implemented by the class StateService. The service implementation defines the InstanceContextMode.PerSession to keep state with the instance:

  [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
  public class StateService: IStateService
  {
    int i = 0;
        
    public void Init(int i)
    {
      this.i = i;
    }
        
    public void SetState(int i)
    {
      this.i = i;
    }
        
    public int GetState()
    {
      return i;
    }
        
    public void Close()
    {
    }
  }

Now the binding to the address and protocol must be defined. Here, the basicHttpBinding is assigned to the endpoint of the service:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="StateServiceSample.Service1Behavior"
        name="Wrox.ProCSharp.WCF.StateService">
        <endpoint address="" binding="basicHttpBinding"
            bindingConfiguration=""
            contract="Wrox.ProCSharp.WCF.IStateService">
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding"
            contract="IMetadataExchange" />
        <host>
          <baseAddresses>
             <add baseAddress="http://localhost:8731/Design_Time_Addresses/
                               StateServiceSample/Service1/" />
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="StateServiceSample.Service1Behavior">
          <serviceMetadata httpGetEnabled="True"/>
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

If you start the service host with the defined configuration, an exception of type InvalidOperationException is thrown. The error message with the exception gives this error message: Contract Requires Session, but Binding ‘BasicHttpBinding’ Doesn’t Support It or Isn’t Configured Properly to Support It.

Not all bindings support all services. Because the service contract requires a session with the attribute [ServiceContract(SessionMode=SessionMode.Required)], the host fails because the configured binding does not support sessions.

As soon as you change the configuration to a binding that supports sessions (for example, the wsHttpBinding), the server starts successfully:

      <endpoint address="" binding="wsHttpBinding"
        bindingConfiguration=""
        contract="Wrox.ProCSharp.WCF.IStateService">
      </endpoint>

With the implementation of the service, you can apply the properties in the following table to the service methods, with the attribute OperationBehavior.

OPERATIONBEHAVIOR PROPERTY DESCRIPTION
AutoDisposeParameters By default, all disposable parameters are automatically disposed. If the parameters should not be disposed, you can set the property AutoDisposeParameters to false. Then the sender is responsible for disposing the parameters.
Impersonation With the Impersonation property, the caller can be impersonated, and the method runs with the identity of the caller.
ReleaseInstanceMode The InstanceContextMode defines the lifetime of the object instance with the service behavior setting. With the operation behavior setting, you can override the setting based on the operation. The ReleaseInstanceMode defines an instance release mode with the enumeration ReleaseInstanceMode. The value None uses the instance context mode setting. With the values BeforeCall, AfterCall, and BeforeAndAfterCall, you can define recycle times with the operation.
TransactionScopeRequired With the property TransactionScopeRequired, you can specify if a transaction is required with the operation. If a transaction is required, and the caller already flows a transaction, the same transaction is used. If the caller doesn’t flow a transaction, a new transaction is created.
TransactionAutoComplete The TransactionAutoComplete property specifies whether the transaction should complete automatically. If the TransactionAutoComplete property is set to true, the transaction is aborted if an exception is thrown. The transaction is committed if it is the root transaction and no exception is thrown.

BINDING

A binding describes how a service wants to communicate. With binding, you can specify the following features:

  • Transport protocol
  • Security
  • Encoding format
  • Transaction flow
  • Reliability
  • Shape change
  • Transport upgrade

Standard Bindings

A binding is composed of multiple binding elements that describe all binding requirements. You can create a custom binding or use one of the predefined bindings that are shown in the following table.

STANDARD BINDING DESCRIPTION
BasicHttpBinding BasicHttpBinding is the binding for the broadest interoperability, the first-generation web services. Transport protocols used are HTTP or HTTPS; security is available only from the transport protocol.
WSHttpBinding WSHttpBinding is the binding for the next-generation web services, platforms that implement SOAP extensions for security, reliability, and transactions. The transports used are HTTP or HTTPS; for security the WS-Security specification is implemented; transactions are supported, as has been described, with the WS-Coordination, WS-AtomicTransaction, and WS-BusinessActivity specifications; reliable messaging is supported with an implementation of WS-ReliableMessaging. WS-Profile also supports Message Transmission Optimization Protocol (MTOM) encoding for sending attachments. You can find specifications for the WS-* standards at http://www.oasis-open.org.
WS2007HttpBinding WS2007HttpBinding derives from the base class WSHttpBinding and supports security, reliability, and transaction specifications defined by Organization for the Advancement of Structured Information Standards (OASIS). This class offers newer SOAP standards.
WSHttpContextBinding WSHttpContextBinding derives from the base class WSHttpBinding and adds support for a context without using cookies. This binding adds a ContextBindingElement to exchange context information. The context binding element was needed with Workflow Foundation 3.0.
WebHttpBinding This binding is used for services that are exposed through HTTP requests instead of SOAP requests. This is useful for scripting clients — for example, ASP.NET AJAX.
WSFederationHttpBinding WSFederationHttpBinding is a secure and interoperable binding that supports sharing identities across multiple systems for authentication and authorization.
WSDualHttpBinding The binding WSDualHttpBinding, in contrast to WSHttpBinding, supports duplex messaging.
NetTcpBinding All standard bindings prefixed with the name Net use a binary encoding used for communication between .NET applications. This encoding is faster than the text encoding with WSxxx bindings. The binding NetTcpBinding uses the TCP/IP protocol.
NetTcpContextBinding Similar to WSHttpContextBinding, NetTcpContextBinding adds a ContextBindingElement to exchange context with the SOAP header.
NetHttpBinding This is a new binding with .NET 4.5 to support the Web Socket transport protocol.
NetPeerTcpBinding NetPeerTcpBinding provides a binding for peer-to-peer communication.
NetNamedPipeBinding NetNamedPipeBinding is optimized for communication between different processes on the same system.
NetMsmqBinding The binding NetMsmqBinding brings queued communication to WCF. Here, the messages are sent to the message queue.
MsmqIntegrationBinding MsmqIntegrationBinding is the binding for existing applications that uses message queuing. In contrast, the binding NetMsmqBinding requires WCF applications both on the client and server.
CustomBinding With a CustomBinding the transport protocol and security requirements can be completely customized.

Features of Standard Bindings

Depending on the binding, different features are supported. The bindings starting with WS are platform-independent, supporting web services specifications. Bindings that start with the name Net use binary formatting for high-performance communication between .NET applications. The new NetHttpBinding changes the naming conventions because it does not require .NET applications on both sides of the wire it’s based on the Web Socket standard.

Other features are support of sessions, reliable sessions, transactions, and duplex communication; the following table lists the bindings supporting these features.

FEATURE BINDING
Sessions WSHttpBinding, WSDualHttpBinding, WsFederationHttpBinding, NetTcpBinding, NetNamedPipeBinding
Reliable Sessions WSHttpBinding, WSDualHttpBinding, WsFederationHttpBinding, NetTcpBinding
Transactions WSHttpBinding, WSDualHttpBinding, WSFederationHttpBinding, NetTcpBinding, NetNamedPipeBinding, NetMsmqBinding, MsmqIntegrationBinding
Duplex Communication WsDualHttpBinding, NetTcpBinding, NetNamedPipeBinding, NetPeerTcpBinding

Along with defining the binding, the service must define an endpoint. The endpoint is dependent on the contract, the address of the service, and the binding. In the following code sample, a ServiceHost object is instantiated, and the address http://localhost:8080/RoomReservation, a WsHttpBinding instance, and the contract are added to an endpoint of the service:

  static ServiceHost host;
      
  static void StartService()
  {
    var baseAddress = new Uri("http://localhost:8080/RoomReservation");
    host = new ServiceHost(typeof(RoomReservationService));
      
    var binding1 = new WSHttpBinding();
    host.AddServiceEndpoint(typeof(IRoomService), binding1, baseAddress);
    host.Open();
  }

In addition to defining the binding programmatically, you can define it with the application configuration file. The configuration for WCF is placed inside the element <system.serviceModel>. The <service> element defines the services offered. Similarly, as you’ve seen in the code, the service needs an endpoint, and the endpoint contains address, binding, and contract information. The default binding configuration of wsHttpBinding is modified with the bindingConfiguration XML attribute that references the binding configuration wsHttpConfig1. This is the binding configuration you can find inside the <bindings> section, which is used to change the wsHttpBinding configuration to enable reliableSession.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Wrox.ProCSharp.WCF.RoomReservationService">
        <endpoint address=" http://localhost:8080/RoomReservation"
            contract="Wrox.ProCSharp.WCF.IRoomService"
            binding="wsHttpBinding" bindingConfiguration="wsHttpBinding" />
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="wsHttpBinding">
          <reliableSession enabled="true" />
        </binding>
      </wsHttpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

Web Socket

WebSocket is a new communication protocol based on TCP. The HTTP protocol is stateless. With HTTP the server can close the connection every time it answers the request. If a client wants to receive ongoing information from the server, this always had some issues with the HTTP protocol.

Because the HTTP connection is kept, one way to deal with this would be to have a service running on the client, and the server connects to the client and sends responses. If a firewall is between the client and the server, this usually doesn’t work because the firewall blocks incoming requests.

Another way to deal with this is to use another protocol than the HTTP protocol. The connection can stay alive. The issue with other protocols is that the port needs to be opened with the firewall. Firewalls are always an issue, but they are needed to keep the bad folks out.

The way such a scenario was usually done is by instantiating the request every time from the client. The client polls the server to ask if there’s something new. This works but has the disadvantage that either the client asks too many times for news when there is none and thus increases the network traffic, or the client does get old information.

A new solution for this scenario is the WebSocket protocol. This protocol is defined by the W3C (http://www.w3.org/TR/websockets) and starts with an HTTP request. Starting with an HTTP request from the client, the firewall usually allows the request. The client starts with a GET request with Upgrade: websocket Connection: Upgrade in the HTTP header, along with the WebSocket version and security information. If the server supports the WebSocket protocol, the server answers with an upgrade and switches from HTTP to the WebSocket protocol.

With WCF the two bindings new with .NET 4.5 support the WebSocket protocol: netHttpBinding, and netHttpsBinding.

Now get into a sample to make use of the WebSocket protocol. Start with an empty web application used to host the service.

The default binding for the HTTP protocol is the basicHttpBinding as you’ve seen earlier. This can be changed defining the protocolMapping to specify the netHttpBinding as shown. This way it’s not necessary to configure the service element to match the contract, binding, and address to an endpoint. With the configuration, serviceMetadata is enabled to allow the client referencing the service with the Add Service Reference dialog (configuration file WebSocketsSample/web.config).

  <system.serviceModel>
    <protocolMapping>
      <remove scheme="http" />
      <add scheme="http" binding="netHttpBinding" />
      <remove scheme="https" />
      <add scheme="https" binding="netHttpsBinding" />
    </protocolMapping>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
  </system.serviceModel> 

The service contract is defined by the interfaces IDemoServices and IDemoCallback (code file WebSocketsSample/IDemoService.cs). IDemoService is the service interface that defines the method StartSendingMessages. The client invokes the method StartSendingMessages to start the process that the service can return messages to the client. The client therefore needs to implement the interface IDemoCallback. This interface is invoked by the server and implemented by the client.

The methods of the interfaces are defined to return Task. With this the service can easily make use of asynchronous features, but this doesn’t go through to the contract. Defining the methods asynchronously is independent of the WSDL generated.

using System.ServiceModel;
using System.Threading.Tasks;
 
namespace WebSocketsSample
{
  [ServiceContract]
  public interface IDemoCallback
  {
    [OperationContract(IsOneWay = true)]
    Task SendMessage(string message);
  }
 
  [ServiceContract(CallbackContract = typeof(IDemoCallback))]
  public interface IDemoService
  {
    [OperationContract]
    Task StartSendingMessages();
  }
}

The implementation of the service is done in the DemoService class (code file WebSocketsSample/DemoService.cs). Within StartSendindingMessages, the callback interface to go back to the client is retrieved with OperationContext.Current.GetCallbackChannel. When the client invokes the method, it returns immediately as soon as the first time the SendMessage method is invoked. The thread is not blocked until the SendMessage method completes. With await a thread just comes back to the StartSendingMessages when the SendMessage is completed. Then a delay of 1 second is done before the client receives another message. In case the communication channel is closed the while loop exits.

using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Threading.Tasks;
 
namespace WebSocketsSample
{
  public class DemoService : IDemoService
  {
    public async Task StartSendingMessages()
    {
      IDemoCallback callback = 
        OperationContext.Current.GetCallbackChannel<IDemoCallback>();
      int loop = 0;
      while ((callback as IChannel).State == CommunicationState.Opened)
      {
        await callback.SendMessage(string.Format(
          "Hello from the server {0}", loop++));
        await Task.Delay(1000);
      }
    }
  }
}

The client application is created as a console application. Because metadata is available with the service, adding a service reference creates a proxy class that can be used to call the service and also to implement the callback interface. Adding the service reference not only creates the proxy class, but also adds the netHttpBinding to the configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.serviceModel>
    <bindings>
      <netHttpBinding>
        <binding name="NetHttpBinding_IDemoService">
          <webSocketSettings transportUsage="Always" />
        </binding>
      </netHttpBinding>
    </bindings>
    <client>
      <endpoint address="ws://localhost:20839/DemoService.svc" 
        binding="netHttpBinding" 
        bindingConfiguration="NetHttpBinding_IDemoService" 
        contract="DemoService.IDemoService"
        name="NetHttpBinding_IDemoService" />
    </client>
  </system.serviceModel>
</configuration>

The implementation of the callback interface just writes a message to the console with the information received from the service. To start all the processing, a DemoServiceClient instance is created that receives an InstanceContext object. The InstanceContext object contains an instance to the CallbackHandler, a reference retrieved by the service to go back to the client.

using System;
using System.ServiceModel;
using ClientApp.DemoService;
 
namespace ClientApp
{
  class Program
  {
    private class CallbackHandler : IDemoServiceCallback
    {
      public void SendMessage(string message)
      {
        Console.WriteLine("message from the server {0}", message);
      }
    }
 
    static void Main(string[] args)
    {
      Console.WriteLine("client... wait for the server");
      Console.ReadLine();
      StartSendRequest();
      Console.WriteLine("next return to exit");
      Console.ReadLine();
    }
 
    static async void StartSendRequest()
    {
      var callbackInstance = new InstanceContext(new CallbackHandler());
      var client = new DemoServiceClient(callbackInstance);
      await client.StartSendingMessagesAsync();
    }
  }
}

Running the application, the client requests the messages from the service, and the service responds independent of the client:

client... wait for the server
 
next return to exit
message from the server Hello from the server 0
message from the server Hello from the server 1
message from the server Hello from the server 2
message from the server Hello from the server 3
message from the server Hello from the server 4
 
Press any key to continue . . .

HOSTING

WCF is flexible when you are choosing a host to run the service. The host can be a Windows service, a COM+ application, WAS (Windows Activation Services) or IIS, a Windows application, or just a simple console application. When creating a custom host with Windows Forms or WPF, you can easily create a peer-to-peer solution.

Custom Hosting

Start with a custom host. The sample code shows hosting of a service within a console application; however, in other custom host types, such as Windows services or Windows applications, you can program the service in the same way.

In the Main method, a ServiceHost instance is created. After the ServiceHost instance is created, the application configuration file is read to define the bindings. You can also define the bindings programmatically, as shown earlier. Next, the Open method of the ServiceHost class is invoked, so the service accepts client calls. With a console application, you need to be careful not to close the main thread until the service should be closed. Here, the user is asked to press Return to exit the service. When the user does this, the Close method is called to actually end the service:

using System;
using System.ServiceModel;
        
public class Program
{
  public static void Main()
  {
    using (var serviceHost = new ServiceHost())
    {
      serviceHost.Open();
        
      Console.WriteLine("The service started. Press return to exit");
      Console.ReadLine();
        
      serviceHost.Close();
    }
  }
}

To abort the service host, you can invoke the Abort method of the ServiceHost class. To get the current state of the service, the State property returns a value defined by the CommunicationState enumeration. Possible values are Created, Opening, Opened, Closing, Closed, and Faulted.


NOTE If you start the service from within a Windows Forms or WPF application and the service code invokes methods of Windows controls, you must be sure that only the control’s creator thread is allowed to access the methods and properties of the control. With WCF, this behavior can be achieved easily by setting the UseSynchronizatonContext property of the attribute [ServiceBehavior].

WAS Hosting

With Windows Activation Services (WAS) hosting, you get the features from the WAS worker process such as automatic activation of the service, health monitoring, and process recycling.

To use WAS hosting, you just need to create a website and a .svc file with the ServiceHost declaration that includes the language and the name of the service class. The code shown here is using the class Service1. In addition, you must specify the file that contains the service class. This class is implemented in the same way that you saw earlier when defining a WCF service library.

<%@ServiceHost language="C#" Service="Service1" CodeBehind="Service1.svc.cs" %>

If you use a WCF service library that should be available from WAS hosting, you can create a .svc file that just contains a reference to the class:

<%@ ServiceHost Service="Wrox.ProCSharp.WCF.Services.RoomReservationService" %>

Since the introduction of Windows Vista and Windows Server 2008, WAS enables defining .NET TCP and Message Queue bindings. If you use the previous edition, IIS 6 or IIS 5.1, which is available with Windows Server 2003 and Windows XP, activation from a .svc file can be done only with an HTTP binding.

Preconfigured Host Classes

To reduce the configuration necessities, WCF also offers some hosting classes with preconfigured bindings. One example is located in the assembly System.ServiceModel.Web in the namespace System.ServiceModel.Web with the class WebServiceHost. This class creates a default endpoint for HTTP and HTTPS base addresses if a default endpoint is not configured with the WebHttpBinding. Also, this class adds the WebHttpBehavior if another behavior is not defined. With this behavior, simple HTTP GET and POST, PUT, DELETE (with the WebInvoke attribute) operations can be done without additional setup (code file RoomReservation/RoomReservationWebHost/Program.cs).

using System;
using System.ServiceModel;
using System.ServiceModel.Web;
using Wrox.ProCSharp.WCF.Service;
 
namespace RoomReservationWebHost
{
  class Program
  {
    static void Main()
    {
      var baseAddress = new Uri("http://localhost:8000/RoomReservation");
      var host = new WebServiceHost(typeof(RoomReservationService), baseAddress);
      host.Open();
 
      Console.WriteLine("service running");
      Console.WriteLine("Press return to exit...");
      Console.ReadLine();
 
      if (host.State == CommunicationState.Opened)
        host.Close();
 
    }
  }
}

To use a simple HTTP GET request to receive the reservations, the method GetRoomReservation needs a WebGet attribute to map the method parameters to the input from the GET request. In the following code, a UriTemplate is defined that requires Reservations to be added to the base address followed by From and To parameters. The From and To parameters in turn are mapped to the fromTime and toTime variables (code file RoomReservationService/RoomReservationService.cs).

    [WebGet(UriTemplate="Reservations?From={fromTime}&To={toTime}")]
    public RoomReservation[] GetRoomReservations(DateTime fromTime, 
      DateTime toTime)
    {
      var data = new RoomReservationData();
      return data.GetReservations(fromTime, toTime);
    }

Now the service can be invoked with a simple request as shown. All the reservations for the specified time frame are returned.

http://localhost:8000/RoomReservation/Reservations?From=2012/1/1&To=2012/8/1

NOTE System.Data.Services.DataServiceHost is another class with preconfigured features. This class derives itself from WebServiceHost and offers data services discussed in Chapter 44, “WCF Data Services.”

CLIENTS

A client application needs a proxy to access a service. There are three ways to create a proxy for the client:

  • Visual Studio Add Service Reference — This utility creates a proxy class from the metadata of the service.
  • ServiceModel Metadata Utility tool (Svcutil.exe) — You can create a proxy class with the Svcutil utility. This utility reads metadata from the service to create the proxy class.
  • ChannelFactory class — This class is used by the proxy generated from Svcutil; however, it can also be used to create a proxy programmatically.

Using Metadata

Adding a service reference from Visual Studio requires accessing a WSDL document. The WSDL document is created by a MEX endpoint that needs to be configured with the service. With the following configuration, the endpoint with the relative address mex uses the mexHttpBinding and implements the contract IMetadataExchange. To access the metadata with an HTTP GET request, the behaviorConfiguration MexServiceBehavior is configured.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration=" MexServiceBehavior "
        name="Wrox.ProCSharp.WCF.RoomReservationService">
        <endpoint address="Test" binding="wsHttpBinding"
          contract="Wrox.ProCSharp.WCF.IRoomService" />
        <endpoint address="mex" binding="mexHttpBinding"
          contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress=
        "http://localhost:8733/Design_Time_Addresses/RoomReservationService/" />
          <baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MexServiceBehavior">
          <! – To avoid disclosing metadata information,
          set the value below to false and remove the metadata endpoint above
          before deployment – >
          <serviceMetadata httpGetEnabled="True"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Similar to the Add service reference from Visual Studio, the Svcutil utility needs metadata to create the proxy class. The Svcutil utility can create a proxy from the MEX metadata endpoint, the metadata of the assembly, or WSDL and XSD documentation:

svcutil http://localhost:8080/RoomReservation?wsdl /language:C# /out:proxy.cs
svcutil CourseRegistration.dll
svcutil CourseRegistration.wsdl CourseRegistration.xsd

After the proxy class is generated, it just needs to be instantiated from the client code, the methods need to be called, and finally the Close()method must be invoked:

var client = new RoomServiceClient();
client.RegisterForCourse(roomReservation);
client.Close();

Sharing Types

The generated proxy class derives from the base class ClientBase<TChannel> that wraps the ChannelFactory<TChannel> class. Instead of using a generated proxy class, you can use the ChannelFactory<TChannel> class directly. The constructor requires the binding and endpoint address; next, you can create the channel and invoke methods as defined by the service contract. Finally, the factory must be closed:

       var binding = new WsHttpBinding();
       var address = new EndpointAddress("http://localhost:8080/RoomService");
      
       var factory = new ChannelFactory<IStateService>(binding, address);
      
       IRoomService channel = factory.CreateChannel();
         channel.ReserveRoom(roomReservation);
        
         //.
         factory.Close();

The ChannelFactory<TChannel> class has several properties and methods, as shown in the following table.

CHANNELFACTORY MEMBERS DESCRIPTION
Credentials Credentials is a read-only property to access the ClientCredentials object assigned to the channel for authentication with the service. The credentials can be set with the endpoint.
Endpoint Endpoint is a read-only property to access the ServiceEndpoint associated with the channel. The endpoint can be assigned in the constructor.
State The State property is of type CommunicationState to return the current state of the channel. CommunicationState is an enumeration with the values Created, Opening, Opened, Closing, Closed, and Faulted.
Open() The Open method is used to open the channel.
Close() The Close method closes the channel.
Opening Opened Closing Closed Faulted You can assign event handlers to get informed about state changes of the channel. Events are fired before and after the channel is opened, before and after the channel is closed, and in case of a fault.

DUPLEX COMMUNICATION

The next sample application shows how a duplex communication can be done between the client and the service. The client starts the connection to the service. After the client connects to the service, the service can call back into the client. Duplex communication was shown earlier with WebSocket protocol as well. Instead of using the WebSocket protocol (which are just supported with Windows 8 and Windows Server 2012), duplex communication can also be done with the WsHttpBinding and the NetTcpBinding as shown here.

Contract for Duplex Communication

For duplex communication, a contract must be specified that is implemented in the client. Here the contract for the client is defined by the interface IMyMessageCallback. The method implemented by the client is OnCallback. The operation has the operation contract setting IsOneWay=true applied. This way, the service doesn’t wait until the method is successfully invoked on the client. By default, the service instance can be invoked from only one thread. (See the ConcurrencyMode property of the service behavior, which is, by default, set to ConcurrencyMode.Single.)

If the service implementation now does a callback to the client and waits to get an answer from the client, the thread getting the reply from the client must wait until it gets a lock to the service object. Because the service object is already locked by the request to the client, a deadlock occurs. WCF detects the deadlock and throws an exception. To avoid this situation, you can change the ConcurrencyMode property to the value Multiple or Reentrant. With the setting Multiple, multiple threads can access the instance concurrently. Here, you must implement locking on your own. With the setting Reentrant, the service instance stays single-threaded but enables answers from callback requests to reenter the context. Instead of changing the concurrency mode, you can specify the IsOneWay property with the operation contract. This way, the caller does not wait for a reply. Of course, this setting is possible only if return values are not expected.

The contract of the service is defined by the interface IMyMessage. The callback contract is mapped to the service contract with the CallbackContract property of the service contract definition (code file DuplexCommunication/MessageService/IMyMessage.cs):

  public interface IMyMessageCallback
  {
    [OperationContract(IsOneWay=true)]
    void OnCallback(string message);
  }
        
  [ServiceContract(CallbackContract=typeof(IMyMessageCallback))]
  public interface IMyMessage
  {
    [OperationContract]
    void MessageToServer(string message);
  }

Service for Duplex Communication

The class MessageService implements the service contract IMyMessage. The service writes the message from the client to the console. To access the callback contract, you can use the OperationContext class. OperationContext.Current returns the OperationContext associated with the current request from the client. With the OperationContext, you can access session information, message headers and properties, and, in the case of a duplex communication, the callback channel. The generic method GetCallbackChannel returns the channel to the client instance. This channel can then be used to send a message to the client by invoking the method OnCallback, which is defined with the callback interface IMyMessageCallback. To demonstrate that it is also possible to use the callback channel from the service independently of the completion of the method, a new thread that receives the callback channel is created. The new thread sends messages to the client by using the callback channel (code file DuplexCommunication/MessageService/MessageService.cs).

  public class MessageService: IMyMessage
  {
    public void MessageToServer(string message)
    {
      Console.WriteLine("message from the client: {0}", message);
      IMyMessageCallback callback =
          OperationContext.Current.
              GetCallbackChannel<IMyMessageCallback>();
        
      callback.OnCallback("message from the server");
      
      Task.Factory.StartNew(new Action<object>(TaskCallback), callback);  
    }
        
    private async void ThreadCallback(object callback)
    {
      IMyMessageCallback messageCallback = callback as IMyMessageCallback;
      for (int i = 0; i < 10; i++)
      {
        messageCallback.OnCallback("message " + i.ToString());
        await Task.Delay(1000);
      }
    }
  }

Hosting the service is the same as it was with the previous samples, so it is not shown here. However, for duplex communication, you must configure a binding that supports a duplex channel. One of the bindings supporting a duplex channel is wsDualHttpBinding, which is configured in the application’s configuration file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Wrox.ProCSharp.WCF.MessageService">
        <endpoint contract="Wrox.ProCSharp.WCF.IMyMessage"
            binding="wsDualHttpBinding"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8733/Service1" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Client Application for Duplex Communication

With the client application, the callback contract must be implemented as shown here with the class ClientCallback that implements the interface IMyMessageCallback (code file DuplexCommunication/MessageClient/Program.cs):

  class ClientCallback: IMyMessageCallback
  {
    public void OnCallback(string message)
    {
      Console.WriteLine("message from the server: {0}", message);
    }
  }

With a duplex channel, you cannot use the ChannelFactory to initiate the connection to the service as was done previously. To create a duplex channel, you can use the DuplexChannelFactory class. This class has a constructor with one more parameter in addition to the binding and address configuration. This parameter specifies an InstanceContext that wraps one instance of the ClientCallback class. When passing this instance to the factory, the service can invoke the object across the channel. The client just needs to keep the connection open. If the connection is closed, the service cannot send messages across it.

    private async static void DuplexSample()
    {       
      var binding = new WSDualHttpBinding();
      var address = new EndpointAddress("http://localhost:8733/Service1");
        
      var clientCallback = new ClientCallback();
      var context = new InstanceContext(clientCallback);
        
      var factory = new DuplexChannelFactory<IMyMessage>(context, binding, 
                        address);
        
      IMyMessage messageChannel = factory.CreateChannel();
        
      await Task.Run(() => messageChannel.MessageToServer("From the client"));
    }

Duplex communication is achieved by starting the service host and the client application.

ROUTING

Using the SOAP protocol has some advantages to HTTP GET requests with REST. One of the advanced features that can be done with SOAP is routing. With routing, the client does not directly address the service, but a router in between that forwards the request.

There are different scenarios to use this feature. One is for failover (see Figure 43-10). If the service cannot be reached or returns in an error, the router calls the service on a different host. This is abstracted from the client; the client just receives a result.

Routing can also be used to change the communication protocol (see Figure 43-11). The client can use the HTTP protocol to call a request and sends this to the router. The router acts as a client with the net.tcp protocol and calls a service forwarding the message.

Using routing for scalability is another scenario (see Figure 43-12). Depending on a field of the message header or also information from the message content, the router can decide to forward a request to one or the other server. Requests from customers that start with the letter A–F go to the first server, G–N to the second one, and O–Z to the third.

Sample Application

With the routing sample application, a simple service contract is defined where the caller can invoke the GetData operation from the IDemoService interface (code file RoutingSample/DemoService/IDemoService.cs):

using System.ServiceModel;
 
namespace Wrox.ProCSharp.WCF
{
  [ServiceContract(Namespace="http://www.cninnovation.com/Services/2012")]
  public interface IDemoService
  {
    [OperationContract]
    string GetData(string value);
  }
}

The implementation of the service (code file RoutingSample/DemoService/DemoService.cs) just returns a message with the GetData method. The message contains the information received along a server-side string that is initialized from the host. This way you can see the host that returned the call to the client.

using System;
 
namespace Wrox.ProCSharp.WCF
{
  public class DemoService : IDemoService
  {
    public static string Server { get; set; }
 
    public string GetData(string value)
    {
      string message = string.Format("Message from {0}, You entered: {1}", 
        Server, value);
      Console.WriteLine(message);
      return message;
    }
  }
}

Two sample hosts just create a ServiceHost instance and open it to start the listener. Each of the hosts defined assigns a different value to the Server property of the DemoService.

Routing Interfaces

For routing, WCF defines the interfaces ISimplexDataGramRouter, ISimplexSessionRouter, IRequestReplyRouter, and IDuplexSessionRouter. Depending on the service contract, different interfaces can be used. ISimplexDataGramRouter can be used with operations that have the OperationContract with IsOneWay settings. With ISimplexDatagramRouter, sessions are optional. ISimplexSessionRouter can be used for one-way messages like ISimlexDatagramRouter, but here sessions are mandatory. IRequestReplyRouter is used for the most common scenario, messages with request and response. With duplex communications (for example, with the WsDualHttpBinding used earlier), the interface IDuplexSessionRouter is used.

Depending on the message pattern used, a custom router needs to implement the corresponding router interface.

WCF Routing Service

Instead of creating a custom router, the RouterService from the namespace System.ServiceModel.Routing can be used. This class implements all the routing interfaces and thus can be used with all the message patterns. It can be hosted just like any other service (code file RoutingSample/Router/Program.cs). In the StartService method, a new ServiceHost is instantiated passing the RoutingService type. This is just like the other hosts you’ve seen before.

using System;
using System.ServiceModel;
using System.ServiceModel.Routing;
 
namespace Router
{
  class Program
  {
    internal static ServiceHost routerHost = null;
 
    static void Main()
    {
      StartService();
 
      Console.WriteLine("Router is running. Press return to exit");
      Console.ReadLine();
 
      StopService();
    }
 
    internal static void StartService()
    {
      try
      {
        routerHost = new ServiceHost(typeof(RoutingService));
        routerHost.Faulted += myServiceHost_Faulted;
        routerHost.Open();
      }
      catch (AddressAccessDeniedException)
      {
        Console.WriteLine("either start Visual Studio in elevated admin " +
          "mode or register the listener port with netsh.exe");
      }
    }
 
    static void myServiceHost_Faulted(object sender, EventArgs e)
    {
      Console.WriteLine("router faulted");
    }
 
    internal static void StopService()
    {
      if (routerHost != null && routerHost.State == CommunicationState.Opened)
      {
        routerHost.Close();
      }
    }
  }
}

Using a Router for Failover

More interesting than the hosting code is the configuration (configuration file Router/App.config) of the router. The router acts as a server to the client application and as a client to the service. So both parts need to be configured. The configuration as shown here offers the wsHttpBinding as a server part and uses the wsHttpBinding as a client to connect to the service. The service endpoint needs to specify the contract that is used with the endpoint. With the request-reply operations offered by the service, the contract is defined by the IRequestReplyRouter interface.

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="routingData" 
        name="System.ServiceModel.Routing.RoutingService">
        <endpoint address="" binding="wsHttpBinding" 
          name="reqReplyEndpoint" 
          contract="System.ServiceModel.Routing.IRequestReplyRouter" />
        <endpoint address="mex" binding="mexHttpBinding" 
          contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8000/RoutingDemo/router" />
          </baseAddresses>
        </host>
      </service>
    </services>

The client part of the router defines two endpoints for services. For testing the routing service, you can use one system. Of course, usually the hosts run on a different system. The contract can be set to * to allow all contracts to pass through to the services covered by these endpoints.

    <client>
      <endpoint address="http://localhost:9001/RoutingDemo/HostA" 
        binding="wsHttpBinding" contract="*" name="RoutingDemoService1" />
      <endpoint address="http://localhost:9001/RoutingDemo/HostB" 
        binding="wsHttpBinding" contract="*" name="RoutingDemoService2" />
    </client>

The behavior configuration for the service becomes important for routing. The behavior configuration named routingData is referenced with the service configuration you’ve seen earlier. For routing the routing element must be set with the behavior, and here a routing table is referenced using the attribute filterTableName.

    <behaviors>
      <serviceBehaviors>
        <behavior name="routingData">
          <serviceMetadata httpGetEnabled="True"/>
          <routing filterTableName="routingTable1" />
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

The filter table named routingTable1 contains a filter with the filterType MatchAll. This filter matches with every request. Now every request from the client is routed to the endpoint name RoutingDemoService1. If this service fails and cannot be reached, the backup list takes importance. The backup list named failOver1 defines the second endpoint used in case the first one fails.

    <routing>
      <filters>
        <filter name="MatchAllFilter1" filterType="MatchAll" />
      </filters>
      <filterTables>
        <filterTable name="routingTable1">
          <add filterName="MatchAllFilter1" endpointName="RoutingDemoService1" 
            backupList="failOver1" />
        </filterTable>
      </filterTables>
      <backupLists>
        <backupList name="failOver1">
          <add endpointName="RoutingDemoService2"/>
        </backupList>
      </backupLists>
    </routing>

With the routing server and routing configuration in place, you can start the client that makes a call to a service via the router. If everything is fine, the client gets an answer from the service running in host 1. If you stop host 1, and another request from the client, host 2 takes responsibility and returns an answer.

Bridging for Protocol Changes

If the router should act to change the protocol, you can configure the host to use the netTcpBinding instead of the wsHttpBinding. With the router, the client configuration needs to be changed to reference the other endpoint.

      <endpoint address="net.tcp://localhost:9010/RoutingDemo/HostA" 
        binding="netTcpBinding" contract="*" name="RoutingDemoService1" />

That’s all that needs to be done to change the scenario.

Filter Types

With the sample application, a match-all filter has been used. WCF offers more filter types.

FILTER TYPE DESCRIPTION
Action The Action filter enables filtering depending on the action of the message. See the Action property of the OperationContract.
Address The Address filter enables filtering on the address that is in the To field of the SOAP header.
AddressPrefix The AddressPrefix filter does not match on the complete address but on the best prefix match of the address.
MatchAll The MatchAll filter is a filter that matches every request.
XPath With the XPath message filter, an XPath expression can be defined to filter on the message header. You can add information to the SOAP header with a message contract.
Custom If you need to route depending on the content of the message, a Custom filter type is required. With a custom filter type, you need to create a class that derives from the base class MessageFilter. Initialization of the filter is done with a constructor that takes a string parameter. This string can be passed from the configuration initialization.

If multiple filters apply to a request, priorities can be used with filters. However, it’s best to avoid priorities as this decreases performance.

SUMMARY

In this chapter, you learned how to use Windows Communication Foundation for communication between a client and a server. WCF is platform-independent like ASP.NET Web services, but it offers features similar to .NET Remoting, Enterprise Services, and Message Queuing.

WCF has a heavy focus on contracts to make it easier to isolate developing clients and services, and to support platform independence. It defines three different contract types: service contracts, data contracts, and message contracts. You can use several attributes to define the behavior of the service and its operations.

You saw how to create clients from the metadata offered by the service and also by using the .NET interface contract. You learned the features of different binding options. WCF offers not only bindings for platform independence, but also bindings for fast communication between .NET applications. You’ve seen how to create custom hosts and also make use of the WAS host. You saw how duplex communication is achieved by defining a callback interface, applying a service contract, and implementing a callback contract in the client application.

The next few chapters continue with WCF features. In Chapter 44, “WCF Data Services”, you learn about WCF Data Services, Chapter 45, “Windows Workflow Foundation”, you learn about Windows Workflow Foundation and how WCF is used to communicate with workflow instances. Chapter 46, “Windows Communication Foundation”, makes use of a WCF service with peer-to-peer communication. Chapter 47, “Message Queuing” explains how disconnected Message Queuing features can be used with WCF bindings.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset