Introduction to wcf

Summary

Overview

Windows Communication Foundation WCF is a framework for building and running service-oriented applications (see Service Oriented Architecture). WCF and its System.ServiceModel namespace that represents its primary programming interface, is the successor and unification of most distributed systems technologies used to build distributed applications

The current distributed systems technologies are .NET Remoting, Web Services, Web Service Enhancements (WSE), MSMQ, and Enterprise Services/COM+. WCF unifies all these into one programming model and you no longer have to make an exclusive explicit choice for one of these technologies. For example, with WCF you can build a Web Service with reliable communication that supports sessions and transaction flow and extend it to let you view the raw messages as they flow into the system.

Therefore, WCF offers a single programming model that unifies the features of .NET Remoting, MSMQ, Enterprise Services, Web Services and Web Service Enhancements

WCF Fundamentals

WCF is a set of APIs for creating systems that send messages between clients and services. These messages can be sent over the Intranet and the Internet using common transports such as TCP, HTTP, MSMQ, Web Services, etc. Messages can be sent securely and digitally signed for integrity. Simple message patterns such request/response and more complex patterns such as such as communication over two channels are supported.

Other major features include interoperability with existing Web Services and with system that implement Web Service Enhancements (collectively know as "WS-*" specifications). By adhering to these standards applications that use WCF can communicate with clients and services created on other non-Microsoft platforms.

The followings briefly explains most of the concepts and terms used in WCF:

WCF Architecture

The following illustrates the major components of the WCF architecture (Contracts, Service Runtime, Messaging and Activation & Hosting):

Contracts

Service Runtime

The runtime layer contains behaviours that only occur during the actual operation of the service. For example, throttling controls how many messages are processed. Error behaviour determines what happens when an occur occurs. Metadata behaviour governs how and whether metadata is made available to the outside world. Instance behaviour was discussed previously here, and so on.

Messaging

The messaging layer deals with the possible formats and message patterns of the data. For example, WS Security Channel is an implementation of the WS-Security specification enabling security at the message layer. The WS Reliable Messaging Channel guarantees message delivery. The encoders allow to determine whether the message should be sent as text, XML, or any of the other supported encodings.  HTTP Channel and TCP Channel determine which transport protocol to use while MSMQ channel allows interoperation with MSMQ applications.

Activation & Hosting

A service can be either self-hosted or hosted in the context of another application. A Service can run as an EXE, Windows Service, under COM+ or under IIS.

ABCs

Recall that a  WCF service is a program that exposes a collection of Endpoints. Each endpoint is a portal for communicating with the world. A client is a program that exchanges messages with one or more endpoints. A client may also expose endpoints to receive messages from a service in a duplex message exchange pattern. The following sections describe these fundamentals in more details.

'ABC' is the key to understanding how an endpoint is composed:

WCF represents an endpoint using ServiceEndpoint class which has an EndpointAddress, a Binding, and a ContractDescription, corresponding to the endpoint's Address, Binding, and Contract:

The components of a ServiceEndpoint are described below:

EndpointAddress

An EndpointAddress corresponds to 'where is the service?' An EndpointAddress is basically a URI, an identity and a collection of optional headers used to provide additional addressing information beyond the URI (i.e., differentiating between multiple endpoints with the same URI):

Binding

A Binding which describes how to interact with an endpoint, has a name, namespace and a collection of composable binding elements:

The Binding's Name and Namespace are used to uniquely identify the binding in the service's metadata. The collection of binding elements can be used to describe parts of how the endpoint communicates with the outside world. For example, consider the following collection of binding elements:

This collection of bindings indicates how to interact with the endpoint:

The order and types of binding elements is important: The collection of binding elements is used to build a communication stack ordered according to the order of the binding elements in the binding collection. The last element added to the binding collection corresponds to the bottom component of the communication stack, while the first element added to the binding collection corresponds to the top component of the communication stack. Incoming messages flow through this stack from bottom to top (see figure above), while outgoing messages flow through this stack from top to bottom (see figure above).

Finally note that WCF provides a set of pre-defined bindings that can be used in the majority of scenarios instead of defining custom bindings.

Contracts

A WCF contract is essentially a class with methods that defines what the endpoint does. A contract is similar to .NET Remoting's SAOs or CAOs. Each method is a simple message exchange. ContractDescription class is used to describe WCF contracts and their operations. Each contract (again represented by a  ContractDescription class), has a corresponding OperationDescription that described aspects of the operation such as whether the operation is one-way or request/reply:

A duplex contract defines two logical sets of operations: A set that the Service exposes for the Client to call, and a set that the Client exposes for the Service to call. The programming model to define a duplex contract is to split each set in a separate type (i.e., class or interface) and annotate the contract that represents the service's operations with ServiceContractAttribute referencing the contract that defines the client (or callback) operations.

Behaviours

A ServiceBehaviour is a type that implements IServiceBehaviour and applies to services. Similarly a channel behaviour is a type that implements an IChannelBehaviour and applies to client channels. Behaviours are types that extend or modify Service or Client functionality. Behaviours are participate in the process of building a channel and can modify that channel based on user-specified settings.

Channels and Description

Service-side

A ServcieDescription is a structure that describes a WCF service including the Endpoints exposed by the service, the Behaviours applies to the service, and the type (i.e., class) that implements the service. A ServiceDescription class is used to create metadata, code/config and channels:

You can build this service description by hand, or more commonly, you can create it from a type annotated with certain WCF attributes. The code for this type can either be created by hand or generated from a WSDL document using a WCF tool called svcutil.exe.

Client-side

Similarly on the client side, a ChannelDescription is a structure that describes a WCF's client that can communicate with a specific service endpoint:

Note that unlike ServiceDescription, a ChannelDescription contains only one ServiceEndpoint that represents the target Endpoint with which the client channel will communicate.

WCF Runtime

The WCF Runtime is the set of objects responsible for actually sending and receiving messages. This involves formatting messages, applying security, and transmitting and receiving messages using various protocols. The following explains  the key concepts of the WCF runtime:

Message

A WCF Message is the unit of data exchange between a client and an endpoint. A message is essentially an in-memory representation of a SOAP message InfoSet. Note that a WCF Message is not tied to text XML, rather depending on which encoding mechanism is used, a Message can be serialized using the binary formatter, text XML , or any other custom format.

Channel

There are two categories of channels: Transport channels and Protocol channels. Transport channels handle sending and receiving opaque octet streams using some form of transport protocol such as TCP, UDP or MSMQ. Protocol channels implement a SOAP-based protocol by processing and possibly modifying messages.

Endpoint Listener

An EndpointListener is the run-time equivalent of a ServiceEndpoint. The EndpointAddress, Binding, and Contract of a ServiceEndpoint correspond to the EndpointListener's listening address, message filtering and dispatch, and channel stack, respectively. The EndpointListener contains the Channel stack that is responsible for sending and receiving messages.

ServiceHost and Channel Factory
Service Host

The WCF Runtime is usually created behind the scenes by calling ServiceHost.Open. ServiceHost drives the creation of a ServiceDescription and populates ServiceDescription's ServiceEndpoint collection with Endpoints defined in code or config. ServiceHost then uses ServiceDescription to create the channel stack in the form of an EndpointListener object for each ServiceEndpoint in the ServiceDescription:

Channel Factory

Similarly on the client side, the client runtime is created by ChannelFactory which is the client's equivalent of ServiceHost: ChannelFactory drives the creation of a ChannelDescription based on a Contract type, Binding, and EndpointAddress. It then uses this ChannelDescription to create the client's channel stack.

Unlike the Service runtime, the client runtime does not contain EndpointListeners because a client always initiates connection to the Service so there is no need to "listen" for incoming connections.

Examples

Example 1

The following code shows a very basic example of a working minimal WCF application. The project layout is shown below in the solution explorer - BasicClient is a  WCF client (Console application) that invokes the service implemented by BasicServiceImplementation (Class Library)  which in turn is hosted by BasicHost (WCF Service Library):

        BasicServiceImplementation.csproj

/* The first step is to create a contract for the service that will be shared with the outside world. Recall that a contract is essentially a class, similar to SAO or CAO in .NET Remoting, that defines what the service does. The easiest way to create a contract is by implementing as interface that is annotated with ServiceContract attribute. Methods of the interface
would need to be annotated with OperatonContract attribute as shown below.

Implementing the contract is a simple matter of implementing the methods of the interface.
The implementing class becomes the WCF Service class */


using System.ServiceModel;

[ServiceContract()]
public interface IMyBasicService
{
    [OperationContract()]
    string MyOperation1(string myValue);
}

public class MyBasicService : IMyBasicService
{
    public string MyOperation1(string myValue)
    {
        return "Hello " + myValue;
    }
}

        BasicHost.csporj

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <!-- Services are defined under this the <services> section. As assembly
        can have any number of services, with each service having its own <service>
        configuration section -->

        <services>
            <!-- This section and its content define the service endpoint (ABC) which
            include Address, Binding, and Contract. Note that Attribute name corresponds
            to the fully-qualified name of the type implementing the contract.
            The Contract and Binding are defined in an <endpoint> child element
            while the Address is defined in a <host> child element -->
           
<service name="Basics.MyBasicService">

                <!-- endpoint defines the contract's ABS. The 'address' element defines an address that
                is relative to the address specified in the <host> element. If attribute 'address'
                was empty, then the address in <host> must be the full address -->

                <endpoint address="/MyBasicService" contract="Basics.IMyBasicService" binding="wsHttpBinding"/>
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8080"/>
                    </baseAddresses>
                </host>
            </service>
  
        </services>
    </system.serviceModel>
</configuration>

/* The next step is to run the service and this implies doing three things: configuring, hosting, and opening the service.
A WCF service consists of a contract (defined in BasicService project), and configuration entries that specify behaviours and endpoints associated with that implementation (see <system.serviceModel> in your application configuration file). To define a WCF service, the initialization code is embedded inside a managed application (console application). We then add an endpoint for the service (in this example via configuration entries). And finally we create an instance of ServiceHost and open it to automatically create listeners and start listening
for incoming messages from clients.*/


internal class MyServiceHost
{
    internal static ServiceHost myServiceHost = null;

    internal static void StartService()
    {
        myServiceHost = new ServiceHost(typeof(Basics.MyBasicService));

        // Open myServiceHost
        myServiceHost.Open();

        // Output some debugging information
        DisplayInfo( myServiceHost );
    }

    internal static void StopService()
    {
        //Call StopService from your shutdown logic (i.e. dispose method)
        Trace.WriteLine(" Service state: " + myServiceHost.State.ToString());

        if (myServiceHost.State != CommunicationState.Closed)
        {
            myServiceHost.Close();
            Trace.WriteLine(" Service closed!");
        }
    }

    internal static void DisplayInfo( ServiceHost host )
    {
        // Service description
        ServiceDescription description = myServiceHost.Description;
        Trace.WriteLine("ServiceType: " + description.ServiceType.FullName);
        Trace.WriteLine("Behaviours count: " + description.Behaviors.Count);

        if (description.Endpoints.Count > 0)
        {
            foreach (ServiceEndpoint endpoint in description.Endpoints)
            {
                Trace.WriteLine( "Address: " + endpoint.Address.ToString() );
            }
        }
        else
        {
            Trace.WriteLine("ServiceEndpoints count: " + description.Endpoints.Count );
        }
    }

class Program
{
    static void Main(string[] args)
    {
        MyServiceHost.StartService();

        // The service can now be accessed until user presses <return> to terminate the service
        Console.WriteLine("The service is ready");
        Console.WriteLine("Press <RETURN> to terminate the service");
        Console.ReadLine();

        // Terminate the service
        MyServiceHost.StopService();
    }
}

        BaseClient.csporj

/* A WCF client application is a managed application that uses a WCF client object
to communication with another application.

A WCF client object is an object that represents a WCF Service in a form that the client
can use locally to communicate with the remote service. In other words, a WCF client object
is a proxy to the remote service.
 
There are two types of WCF client objects:
1. Objects that extend ClientBase
2. Channel objects returned from a call to ChannelFactory.CreateChannel. Because these objects
implement interfaces, they directly model the inputs and outputs of abstract services in managed
form and are implemented dynamically by the WCF client runtime */
class WCFClientApp
{
    static void Main(string[] args)
    {
        // Create a channel to connect to the remote contract
        ChannelFactory<IMyBasicService> factory = new ChannelFactory<IMyBasicService>(
                                        new WSHttpBinding(),
                                        new EndpointAddress("http://localhost:8080/MyBasicService"));
        IMyBasicService channel = factory.CreateChannel();

        // Invoke methods on the contract
        string strParam = "WCF";
        Console.WriteLine("Sent parameter: " + strParam);
        string strReturnValue = channel.MyOperation1(strParam);
        Console.WriteLine("Received return value: " + strReturnValue);

        // Terminate service when user pressesn any key
        Console.WriteLine("Press <RETURN> to terminate the service");
        Console.ReadLine();
        factory.Close();
    }
}

[ServiceContract()]
public interface IMyBasicService
{
    [OperationContract()]
   
string MyOperation1(string myValue);
}

To run this application, start two instances of VS.NET 2005 and open the Basics solution (which contains the three projects mentioned earlier).  In one instance of VS.NET set BasicHost as the startup project and press F5. In  the other instnace of VS.NET set BasicClient as the startup project and press F5. You can now step through client and server code as you would debug any client-server application. Outputs from both projects are shown below:

Example 2

This example is similar to the Example 1 except that SVCUTIL.EXE is used to generate the proxy used by the client. All projects remain the same except for BasicClient.csproj which is now replaced by BasicClientWithProxy.csproj:

Note that BasicClientWithProxy project contains the file schemas.microsoft.com.2003.10.Serialization.cs which was generated by the SVCUTIL.EXE tool as follows (the tool was executed in output directory of BasicHost. This directory contains the outputs from both BasicServiceImplementation and BasicHost projects):

Note: svcutil also generated a .config file which I had to modify as below. The generated .config file contained a detailed wsHttpBinding which was not properly matched to the server-side binding (I had to modify it as follows):

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <client>
            <endpoint address="http://localhost:8080/MyBasicService" contract="IMyBasicService" binding="wsHttpBinding"/>
        </client>
    </system.serviceModel>
</configuration>

class Program
{
    static void Main(string[] args)
    {
        // Create an instance of the proxy
        MyBasicServiceClient proxy = new MyBasicServiceClient();
        string strReturnValue = proxy.MyOperation1("yazan");
        Console.WriteLine("Received return value: " + strReturnValue);

        // Terminate service when user presses any key
        Console.WriteLine("Press <RETURN> to terminate the service");
        Console.ReadLine();
    }
}