Windows Communication Foundation Architecture Overview
This document provides a high-level view of WCF's
architecture. It is intended to explain WCF's key concepts and how they fit
together. There are a few code examples to further illustrate the concepts but
code is not an emphasis of this document.
The rest of this document is organized in two main sections:
·
WCF Fundamentals: Covers WCF's key concepts, terms and architectural
components.
·
Code Examples: Provides a few short code examples intended to illustrate
and reify the concepts covered in WCF Fundamentals.
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 an Endpoint to receive Messages from a
Service in a duplex message exchange pattern.
The following sections describe these fundamentals in more
detail.
A Service Endpoint has an Address, a Binding
and a Contract.
The Endpoint's Address is a network address where the Endpoint resides. The EndpointAddress class
represents a WCF Endpoint Address.
The Endpoint's Binding specifies how
the Endpoint communicates with the world including things like transport
protocol (e.g. TCP, HTTP), encoding (e.g. text, binary), and security
requirements (e.g. SSL, SOAP message security). The Binding class
represents a WCF Binding.
The Endpoint's Contract specifies what
the Endpoint communicates and is essentially a collection of messages organized
in operations that have basic Message Exchange Patterns (MEPs) such as one-way,
duplex and request/reply. The ContractDescription class represents a WCF
Contract.
The ServiceEndpoint class represents an Endpoint and has an EndpointAddress,
a Binding and a ContractDescription corresponding to the Endpoint's Address,
Binding and Contract respectively (see Figure 1).
Figure 1 Each Service's Endpoint
contains an EndpointAddress, a Binding and a Contract represented by
ContractDescription.
EndpointAddress
An EndpointAddress is basically a URI, an Identity and a
collection of optional headers as shown in Figure 2.
An Endpoint’s security identity is normally it’s URI, however in advanced scenarios the identity can be
explicitly set independent of the URI using the Identity address property.
The optional headers are used to provide additional
addressing information beyond the Endpoint's URI. For example, address headers
are useful for differentiating between multiple Endpoints that share the same
address URI.
Figure 2 EndpointAddress contains a URI and
AddressProperties which contains an Identity and a collection of
AddressHeaders.
Bindings
A Binding has a name and namespace and a collection of composable
binding elements (Figure 3). The Binding's name and namespace uniquely identify
it in the service's metadata. Each binding element describes an aspect of how
the Endpoint communicates with the world.
|
|
|
|

|

For example, Figure 4 shows a binding element collection containing three
binding elements. The presence of each binding element describes part of the how
of communicating with the Endpoint. The TcpTransportBindingElement indicates
that the Endpoint will communicate with the world using TCP as the transport
protocol. ReliableSessionBindingElement indicates that the Endpoint uses
reliable messaging to provide message delivery assurances.
SecurityBindingElement indicates that the Endpoint uses SOAP message security.
Each binding element usually has properties that further describe the specifics
of the how of communicating with the Endpoint. For example, the
ReliableSessionBindingElement has an Assurances property that specifies the
required message delivery assurances, such as none, at least once, at most once
or exactly once.
Figure 4 An
example Binding with three binding elements.
Contracts
A WCF Contract is a collection of Operations that specifies what
the Endpoint communicates to the outside world. Each operation is a simple
message exchange, for example one-way or request/reply message exchange.
The ContractDescription class is used to describe WCF
Contracts and their operations. Within a ContractDescription, each Contract
operation has a corresponding OperationDescription that describes aspects of
the operation such as whether the operation is one-way or request/reply. Each
OperationDescription also describes the messages that make up the operation
using a collection of MessageDescriptions.
A ContractDescription is usually created from an interface
or class that defines the Contract using WCF's programming model. This type is
annotated with ServiceContractAttribute and its methods that correspond to
Endpoint Operations are annotated with OperationContractAttribute. You can also
build a ContractDescription by hand without starting with a CLR type annotated
with attributes.
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 for defining a duplex Contract is to split each set in a separate type
(each type must be a class or an interface) and annotate the contract that
represents the service’s operations with ServiceContractAttribute, referencing
the contract that defines the client (or callback) operations. In addition,
ContractDescription contains a reference to each of the types thereby grouping
them into one duplex Contract.
Similar to Bindings, each Contract has a Name and Namespace
that uniquely identify it in the Service's metadata.
Each Contract also has a collection of ContractBehaviors
which are modules that modify or extend the contract’s behavior. The next
section covers behaviors in more detail.
Figure 5 ContractDescription class describes a WCF
Contract.
Behaviors are types that modify or extend Service or Client
functionality. For example, the metadata behavior, implemented by
ServiceMetadataBehavior, controls whether the Service publishes metadata.
Similarly the security behavior controls impersonation and authorization while
the transactions behavior controls enlisting in, and auto completing
transactions.
Behaviors also participate in the process of building the
channel and can modify that channel based on user-specified settings and/or
other aspects of the Service or Channel.
A Service Behavior is a type that implements
IServiceBehavior and applies to Services. Similarly, a Channel Behavior
is a type that implements IChannelBehavior and applies to Client Channels.
The ServiceDescription class is an in memory structure that
describes a WCF Service including the Endpoints exposed by the Service, the
Behaviors applied to the Service and the type (a class) that implements the
Service (see Figure 6). ServiceDescription is used to create metadata,
code/config and channels.
You can build this ServiceDescription object by hand. You
can also create it from a type annotated with certain WCF attributes, which is
the more common scenario. The code for this type can be written by hand or
generated from a WSDL document using a WCF tool called svcutil.exe.
Although ServiceDescription objects can be created and
populated explicitly, they are often created behind the scenes as part of
running the Service.
Figure 6 ServiceDescription
Object Model.
Similarly on the client side, a ChannelDescription describes
a WCF Client's Channel to a specific Endpoint (Figure 7). The
ChannelDescription class has a collection of IChannelBehaviors which are
Behaviors applied to the Channel. It also has a ServiceEndpoint that describes
the Endpoint with which the Channel will communicate.
Note that, unlike ServiceDescription, ChannelDescription
contains only one ServiceEndpoint that represents the target Endpoint with
which the Channel will communicate.
Figure 7 ChannelDescription
object model.
The WCF runtime is the set of objects responsible for
sending and receiving messages. For example, things like formatting messages,
applying security and transmitting and receiving messages using various
transport protocols as well as dispatching received messages to the appropriate
operation all fall within the WCF runtime. The following sections explain the
key concepts of the WCF runtime.
Message
The 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 Message is not tied to text XML. Rather,
depending on which encoding mechanism is used, a Message can be serialized
using WCF’s binary format, text XML, or any other custom format.
Channels
Channels are the core abstraction for sending Messages to
and receiving Messages from an Endpoint. Broadly speaking, there are two
categories of Channels: Transport Channels handle sending or receiving opaque
octet streams using some form of transport protocol such as TCP, UDP, or MSMQ.
Protocol Channels on the other hand implement a SOAP-based protocol by processing
and possibly modify messages. For example, the security Channel adds and
processes SOAP message headers and may modify the body of the message by
encrypting it. Channels are composable such that a Channel may be layered on
top of another Channel that is in turn layered on top of a third Channel.
EndpointListener
An EndpointListener is the runtime equivalent of a
ServiceEndpoint. The EndpointAddress, Contract and Binding of ServiceEndpoint
(representing where, what and how), 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 the sending and receiving messages.
ServiceHost and ChannelFactory
The WCF Service runtime is usually created behind the scenes
by calling ServiceHost.Open. ServiceHost (Figure 8) drives the creation of a
ServiceDescription from on the Service type and populating the
ServiceDescription’s ServiceEndpoint collection with Endpoints defined in
config or code or both. ServiceHost then uses the ServiceDescription to create
the channel stack in the form of an EndpointListener object for each
ServiceEndpoint in the ServiceDescription.
Figure 8 ServiceHost object
model.
Similarly, on the client side, the Client’s runtime is
created by a ChannelFactory which is the Client’s equivalent of ServiceHost.
ChannelFactory drives the creation of a ChannelDescription
based on a Contract type, a Binding and an 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.
This section provides code examples that show how Services
and Clients are built. These examples are intended to reify the above concepts
and not to teach WCF programming.
As mentioned above, the easiest way to define a contract is
creating an interface or a class and annotating it with
ServiceContractAttribute allowing the system to easily create from it a
ContractDescription.
When using interfaces or classes to define contracts, each
interface or class method that is a member of the contract must be annotated
with OperationContractAttribute. For example:
using System.ServiceModel;
//a
WCF contract defined using an interface
[ServiceContract]
public interface IMath
{
[OperationContract]
int Add(int x, int y);
}
Implementing the contract in this case is simply a matter of
creating a class that implements IMath. That class becomes the WCF Service
class. For example:
//the
service class implements the interface
public class MathService : IMath
{
public int Add(int x, int y)
{ return x + y; }
}
Endpoints can be defined in code or in config. In the
example below, the DefineEndpointImperatively method shows the easiest way to
define Endpoints in code and start the service.
DefineEndpointInConfig method shows the equivalent
endpoint defined in config (config example follows the code below).
public class WCFServiceApp
{
public void DefineEndpointImperatively()
{
//create a service host for MathService
ServiceHost sh = new
ServiceHost(typeof(MathService));
//use the AddEndpoint helper method to
//create the ServiceEndpoint and add it
//to the ServiceDescription
sh.AddServiceEndpoint(
typeof(IMath), //contract type
new WSHttpBinding(), //one of the built-in bindings
"http://localhost/MathService/Ep1"); //the
endpoint's address
//create and open the service runtime
sh.Open();
}
public void DefineEndpointInConfig()
{
//create a service host for MathService
ServiceHost sh = new
ServiceHost (typeof(MathService));
//create and open the service runtime
sh.Open();
}
}
<!-- configuration file used by above code -->
<configuration>
<system.serviceModel>
<services>
<!-- service element references the service type
-->
<service type="MathService">
<!-- endpoint element defines the ABC’s of the
endpoint -->
<endpoint
address="http://localhost/MathService/Ep1"
binding="wsHttpBinding"
contract="IMath"/>
</service>
</services>
</system.serviceModel>
</configuration>
The code below shows two ways to send a message to the IMath
endpoint. SendMessageToEndpoint hides the Channel creation which happens behind
the scenes while SendMessageToEndpointUsingChannel example does it explicitly.
The first example in SendMessageToEndpoint uses a tool named
svcutil.exe and the Service’s metadata to generate a Contract (IMath in this
example), a proxy class (MathProxy in this example) that implements the
Contract, and associated config (not shown here). Again, the Contract defined
by IMath specifies the what (i.e. the operations that can be performed)
while the generated config contains a Binding (the how) and an address
(the where).
Using this proxy class is simply a matter of instantiating
it and calling the Add method. Behind the scenes, the proxy class will create a
Channel and use that it to communicate with the Endpoint.
The second example in SendMessageToEndpointsUsingChannel
below shows communicating with an Endpoint using ChannelFactory directly. In
this example, instead of using a proxy class and config, a Channel is created
directly using ChannelFactory<IMath>.CreateChannel. Also, instead of
using config to define the Endpoint’s address and Binding, the
ChannelFactory<IMath> constructor takes those two pieces of information
as parameters. The third piece of information required to define an Endpoint,
namely the Contract, is passed in as the type T.
using
System.ServiceModel;
//this
contract is generated by svcutil.exe
//from
the service's metadata
public interface IMath
{
[OperationContract]
public int Add(int x, int y)
{ return x + y; }
}
//this
class is generated by svcutil.exe
//from
the service's metadata
//generated
config is not shown here
public class MathProxy : IMath
{
...
}
public class WCFClientApp
{
public void SendMessageToEndpoint()
{
//this uses a proxy class that was
//created by svcutil.exe from the service's metadata
MathProxy proxy = new
MathProxy();
int result = proxy.Add(35,
7);
}
public void
SendMessageToEndpointUsingChannel()
{
//this uses ChannelFactory to create the channel
//you must specify the address, the binding and
//the contract type (IMath)
ChannelFactory<IMath>
factory=new ChannelFactory<IMath>(
new WSHttpBinding(),
new EndpointAddress("http://localhost/MathService/Ep1"));
IMath channel=factory.CreateChannel();
int
result=channel.Add(35,7);
factory.Close();
}
}
Defining a custom Behavior is a matter of implementing
IServiceBehavior (or IChannelBehavior for client-side behaviors). The code
below shows an example behavior that implements IServiceBehavior. In
IServiceBehavior.ApplyBehavior, the code inspects the ServiceDescription and
writes out the Address, Binding and Contract of each ServiceEndpoint as well as
the name of each Behavior in the ServiceDescription.
This particular behavior is also an attribute (inherits from
System.Attribute) making it possible to apply declaratively as will be shown
below. However, behaviors are not required to be attributes.
[AttributeUsageAttribute(
AttributeTargets.Class,
AllowMultiple=false,
Inherited=false)]
public class InspectorBehavior
: System.Attribute,
System.ServiceModel.IServiceBehavior
{
public void ApplyBehavior(
ServiceDescription description,
Collection<DispatchBehavior>
behaviors)
{
Console.WriteLine("-------- Endpoints ---------");
foreach (ServiceEndpoint endpoint in
description.Endpoints)
{
Console.WriteLine("--> Endpoint");
Console.WriteLine("Endpoint Address: {0}",
endpoint.Address);
Console.WriteLine("Endpoint Binding: {0}",
endpoint.Binding.GetType().Name);
Console.WriteLine("Endpoint Contract: {0}",
endpoint.Contract.ContractType.Name);
Console.WriteLine();
}
Console.WriteLine("-------- Service Behaviors --------");
foreach (IServiceBehavior behavior in
description.Behaviors)
{
Console.WriteLine("--> Behavior");
Console.WriteLine("Behavior: {0}",
behavior.GetType().Name);
Console.WriteLine();
}
}
}
All behaviors can be applied imperatively by adding an
instance of the behavior to the ServiceDescription (or the ChannelDescription
on the client side). For example, to apply the InspectorBehavior imperatively
you would write:
ServiceHost sh = new ServiceHost(typeof(MathService));
sh.AddServiceEndpoint(
typeof(IMath),
new WSHttpBinding(),
"http://localhost/MathService/Ep1");
//Add
the behavior imperatively
InspectorBehavior behavior = new
InspectorBehavior();
sh.Description.Behaviors.Add(behavior);
sh.Open();
Additionally, behaviors that inherit from System.Attribute
may be applied declaratively to the service. For example, because
InspectorBehavior inherits from System.Attribute, it can be applied
declaratively like this:
[InspectorBehavior]
public class MathService : IMath
{
public int Add(int x, int y)
{ return x + y; }
}
WCF Services expose a collection of Endpoints where each
Endpoint is a portal for communicating with the world. Each Endpoint has an
Address, a Binding and a Contract (ABC). The Address is where
the Endpoint resides, the Binding is how the
Endpoint communicates and the Contract is what the
Endpoint communicates.
On the Service, a ServiceDescription holds the collection of
ServiceEndpoints each describing an Endpoint that the Service exposes. From
this description, ServiceHost creates a runtime that contains an
EndpointListener for each ServiceEndpoint in the ServiceDescription. The
Endpoint’s address, Binding and Contract (representing the where, what
and how), correspond to the EndpointListener’s listening address,
message filtering and dispatch, and channel stack respectively.
Similarly, on the Client, a ChannelDescription holds the one
ServiceEndpoint with which the Client communicates. From this
ChannelDescription, ChannelFactory creates the channel stack that can
communicate with the Service’s Endpoint.