Wednesday 20 November 2013

WCF Fundamentals



WCF Fundamentals


Comparing ASP.NET Web Services to WCF Based on Development
Windows Communication Foundation (WCF) has an ASP.NET compatibility mode option to enable WCF applications to be programmed and configured like ASP.NET Web services, and mimic their behavior. The following sections compare ASP.NET Web services and WCF based on what is required to develop applications using both technologies.
ASP.NET Web services were developed for building applications that send and receive messages by using the Simple Object Access Protocol (SOAP) over HTTP. The structure of the messages can be defined using an XML Schema, and a tool is provided to facilitate serializing the messages to and from .NET Framework objects. The technology can automatically generate metadata to describe Web services in the Web Services Description Language (WSDL), and a second tool is provided for generating clients for Web services from the WSDL.
WCF is for enabling .NET Framework applications to exchange messages with other software entities. SOAP is used by default, but the messages can be in any format, and conveyed by using any transport protocol. The structure of the messages can be defined using an XML Schema, and there are various options for serializing the messages to and from .NET Framework objects. WCF can automatically generate metadata to describe applications built using the technology in WSDL, and it also provides a tool for generating clients for those applications from the WSDL.

1.6. Bindings

There is multiple aspects of communication with any given service, and there are many possible communication patterns: messages can be synchronous request/reply or asynchronous fire-and-forget; messages can be bidirectional; messages can be delivered immediately or queued; and the queues can be durable or volatile. There are many possible transport protocols for the messages, such as HTTP (or HTTPS), TCP, P2P (peer network), IPC (named pipes), or MSMQ. There are a few possible message encoding options: you can chose plain text to enable interoperability, binary encoding to optimize performance, or MTOM (Message Transport Optimization Mechanism) for large payloads. There are a few options for securing messages: you can choose not to secure them at all, to provide transport-level security only, to provide message-level privacy and security, and of course there are numerous ways for authenticating and authorizing the clients. Message delivery might be unreliable or reliable end-to-end across intermediaries and dropped connections, and the messages might be processed in the order they were sent or in the order they were received. Your service might need to interoperate with other services or clients that are only aware of the basic web service protocol, or they may be capable of using the score of WS-* modern protocols such as WS-Security and WS-Atomic Transactions. Your service may need to interoperate with legacy clients over raw MSMQ messages, or you may want to restrict your service to interoperate only with another WCF service or client.
If you start counting all the possible communication and interaction options, the number of permutations is probably in the tens of thousands. Some of those choices may be mutually exclusive, and some may mandate other choices. Clearly, both the client and the service must be aligned on all these options in order to communicate properly. Managing this level of complexity adds no business value to most applications, and yet the productivity and quality implications of making the wrong decisions are severe.
To simplify these choices and make them more manageable, WCF groups together a set of such communication aspects in bindings. A binding is merely a consistent, canned set of choices regarding the transport protocol, message encoding, communication pattern, reliability, security, transaction propagation, and interoperability. Ideally, you would extract all these "plumbing" aspects out of your service code and allow the service to focus solely on the implementation of the business logic. Binding enables you to use the same service logic over drastically different plumbing.
You can use the WCF-provided bindings as is, you can tweak their properties, or you can write your own custom bindings from scratch. The service publishes its choice of binding in its metadata, enabling clients to query for the type and specific properties of the binding because the client must use the exact same binding values as the service. A single service can support multiple bindings on separate addresses.

1.6.1. The Standard Bindings

WCF defines nine standard bindings:

Basic binding
Offered by the BasicHttpBinding class, this is designed to expose a WCF service as a legacy ASMX web service, so that old clients can work with new services. When used by the client, this binding enables new WCF clients to work with old ASMX services.

TCP binding
Offered by the NetTcpBinding class, this uses TCP for cross-machine communication on the intranet. It supports a variety of features, including reliability, transactions, and security, and is optimized for WCF-to-WCF communication. As a result, it requires both the client and the service to use WCF.

Peer network binding
Offered by the NetPeerTcpBinding class, this uses peer networking as a transport. The peer network-enabled client and services all subscribe to the same grid and broadcast messages to it. Peer networking is beyond the scope of this book since it requires an understanding of grid topology and mesh computing strategies.

IPC binding
Offered by the NetNamedPipeBinding class, this uses named pipes as a transport for same-machine communication. It is the most secure binding since it cannot accept calls from outside the machine and it supports a variety of features similar to the TCP binding.

Web Service (WS) binding
Offered by the WSHttpBinding class, this uses HTTP or HTTPS for transport, and is designed to offer a variety of features such as reliability, transactions, and security over the Internet.

Federated WS binding
Offered by the WSFederationHttpBinding class, this is a specialization of the WS binding, offering support for federated security. Federated security is beyond the scope of this book.

Duplex WS binding
Offered by the WSDualHttpBinding class, this is similar to the WS binding except it also supports bidirectional communication from the service to the client

MSMQ binding
Offered by the NetMsmqBinding class, this uses MSMQ for transport and is designed to offer support for disconnected queued calls.

MSMQ integration binding
Offered by the MsmqIntegrationBinding class, this converts WCF messages to and from MSMQ messages, and is designed to interoperate with legacy MSMQ clients. Using this binding is beyond the scope of this book.

1.6.2. Format and Encoding

Each of the standard bindings uses different transport and encoding, as listed in Table 1-1.
Table 1-1. Transport and encoding for standard bindings (default encoding is in bold)
Name
Transport
Encoding
Interoperable
BasicHttpBinding
HTTP/HTTPS
Text, MTOM
Yes
NetTcpBinding
TCP
Binary
No
NetPeerTcpBinding
P2P
Binary
No
NetNamedPipeBinding
IPC
Binary
No
WSHttpBinding
HTTP/HTTPS
Text, MTOM
Yes
WSFederationHttpBinding
HTTP/HTTPS
Text, MTOM
Yes
WSDualHttpBinding
HTTP
Text, MTOM
Yes
NetMsmqBinding
MSMQ
Binary
No
MsmqIntegrationBinding
MSMQ
Binary
Yes

Having a text-based encoding enables a WCF service (or client) to communicate over HTTP with any other service (or client) regardless of its technology. Binary encoding over TCP or IPC yields the best performance but at the expense of interoperability, by mandating WCF-to-WCF communication.

1.6.3. Choosing a Binding

Choosing a binding for your service should follow the decision-activity diagram shown in Figure 1-4.
Figure 1-4. Choosing a binding

The first question you should ask yourself is whether your service needs to interact with non-WCF clients. If the answer is yes, and if the client is a legacy MSMQ client, choose the MsmqIntegrationBinding that enables your service to interoperate over MSMQ with such a client. If you need to interoperate with a non-WCF client and that client expects basic web service protocol (ASMX web services), choose the BasicHttpBinding, which exposes your WCF service to the outside world as if it were an ASMX web service (that is, a WSI-basic profile). The downside is that you cannot take advantage of most of the modern WS-* protocols. However, if the non-WCF client can understand these standards, choose one of the WS bindings, such as WSHttpBinding, WSFederationBinding, or WSDualHttpBinding. If you can assume that the client is a WCF client, yet it requires offline or disconnected interaction, choose the NetMsmqBinding that uses MSMQ for transporting the messages. If the client requires connected communication, but could be calling across machine boundaries, choose the NetTcpBinding that communicates over TCP. If the client is on the same machine as the service, choose the NetNamedPipeBinding that uses named pipes to maximize performance. You may fine-tune binding selections based on additional criteria such as the need for callbacks (WSDualHttpBinding) or federated security (WSFederationBinding).
Most bindings work well even outside their target scenario. For example, you could use the TCP binding for same-machine or even in-proc communication, and you could use the basic binding for Intranet WCF-to-WCF communication. However, do try to choose a binding according to Figure 1-4.


1.6.4. Using a Binding

Each binding offers literally dozens of configurable properties. There are three modes of working with bindings. You can use the built-in bindings as is if they fit your requirements. You can tweak and configure some of their properties such as transaction propagation, reliability, and security. You can also write your own custom bindings. The most common scenario is using an existing binding almost as is, and merely configuring two or three of its aspects. Application developers will hardly ever need to write a custom binding, but framework developers may need to.

1.7. Endpoints

Every service is associated with an address that defines where the service is, a binding that defines how to communicate with the service, and a contract that defines what the service does. This triumvirate governing the service is easy to remember as the ABC of the service. WCF formalizes this relationship in the form of an endpoint. The endpoint is the fusion of the address, contract, and binding (see Figure 1-5).
Figure 1-5. The endpoint

Every endpoint must have all three elements, and the host exposes the endpoint. Logically, the endpoint is the service's interface, and is analogous to a CLR or COM interface. Note in Figure 1-5 the use of the traditional "lollipop" to denote an endpoint.
Conceptually, even in C# or VB, an interface is an endpoint: the address is the memory address of the type's virtual table, the binding is CLR JIT compiling, and the contract is the interface itself. Because in classic .NET programming you never deal with addresses or bindings, you take them for granted. In WCF the address and the binding are not ordained, and need to be configured.

Every service must expose at least one business endpoint and each endpoint has exactly one contract. All endpoints on a service have unique addresses, and a single service can expose multiple endpoints. These endpoints can use the same or different bindings and can expose the same or different contracts. There is absolutely no relationship between the various endpoints a service provides.
It is important to point out that nothing in the service code pertains to its endpoints and they are always external to the service code. You can configure endpoints either administratively using a config file or programmatically.

1.7.1. Administrative Endpoint Configuration

Configuring an endpoint administratively requires placing the endpoints in the hosting process' config file. For example, given this service definition:
namespace MyNamespace
{
   [ServiceContract]
   interface IMyContract
   {...}
   class MyService : IMyContract
   {...}
}

Example 1-6 shows the required entries in the config file. Under each service type you list its endpoints.
Example 1-6. Administrative endpoint configuration
<system.serviceModel>
   <services>
      <service name = "MyNamespace.MyService">
         <endpoint
            address  = "http://localhost:8000/MyService/"
            binding  = "wsHttpBinding"
            contract = "MyNamespace.IMyContract"
         />
      </service>
   </services>
</system.serviceModel>

When you specify the service and the contract type, you need to use fully qualified type names. I will omit the namespace in the examples throughout the remainder of this book, but you should use a namespace when applicable. Note that if the endpoint provides a base address, then that address schema must be consistent with the binding, such as HTTP with WSHttpBinding. A mismatch causes an exception at the service load time.
Example 1-7 shows a config file defining a single service that exposes multiple endpoints. You can configure multiple endpoints with the same base address as long as the URI is different.
Example 1-7. Multiple endpoints on the same service
<service name = "MyService">
   <endpoint
      address  = "http://localhost:8000/MyService/"
      binding  = "wsHttpBinding"
      contract = "IMyContract"
   />
   <endpoint
      address  = "net.tcp://localhost:8001/MyService/"
      binding  = "netTcpBinding"
      contract = "IMyContract"
    />
    <endpoint
      address  = "net.tcp://localhost:8002/MyService/"
      binding  = "netTcpBinding"
      contract = "IMyOtherContract"
    />
</service>

Administrative configuration is the option of choice in the majority of cases because it provides the flexibility to change the service address, binding, and even exposed contracts without rebuilding and redeploying the service.
1.7.1.1. Using base addresses
In Example 1-7, each endpoint provided its own base address. When you provide an explicit base address, it overrides any base address the host may have provided.
You can also have multiple endpoints use the same base address, as long as the endpoint addresses differ in their URIs:
<service name = "MyService">
   <endpoint
      address  = "net.tcp://localhost:8001/MyService/"
      binding  = "netTcpBinding"
      contract = "IMyContract"
   />
   <endpoint
      address  = "net.tcp://localhost:8001/MyOtherService/"
      binding  = "netTcpBinding"
      contract = "IMyContract"
   />
</service>

Alternatively, if the host provides a base address with a matching transport schema, you can leave the address out, in which case the endpoint address will be the same as the base address of the matching transport:
<endpoint
   binding  = "wsHttpBinding"
   contract = "IMyContract"
/>

If the host does not provide a matching base address, loading the service host will fail with an exception.
When you configure the endpoint address you can add just the relative URI under the base address:
<endpoint
   address  = "SubAddress"
   binding  = "wsHttpBinding"
   contract = "IMyContract"
/>

The endpoint address in this case will be the matching base address plus the URI, and, again, the host must provide a matching base address.
1.7.1.2. Binding configuration
You can use the config file to customize the binding used by the endpoint. To that end, add the bindingConfiguration tag to the endpoint section, and name a customized section in the bindings section of the config file. Example 1-8 demonstrates using this technique to enable transaction propagation. What the transactionFlow tag does will be explained in.
Example 1-8. Service-side binding configuration
<system.serviceModel>
   <services>
      <service name = "MyService">
         <endpoint
            address  = "net.tcp://localhost:8000/MyService/"
            bindingConfiguration = "TransactionalTCP"
            binding  = "netTcpBinding"
            contract = "IMyContract"
         />
         <endpoint
            address  = "net.tcp://localhost:8001/MyService/"
            bindingConfiguration = "TransactionalTCP"
            binding  = "netTcpBinding"
            contract = "IMyOtherContract"
         />
      </service>
   </services>
   <bindings>
      <netTcpBinding>
         <binding name = "TransactionalTCP"
            transactionFlow = "true"
         />
      </netTcpBinding>
   </bindings>
</system.serviceModel>

As shown in Example 1-8, you can reuse the named binding configuration in multiple endpoints simply by referring to it.

1.7.2. Programmatic Endpoint Configuration

Programmatic endpoint configuration is equivalent to administrative configuration. Instead of resorting to a config file, you rely on programmatic calls to add endpoints to the ServiceHost instance. Again, these calls are always outside the scope of the service code. ServiceHost provides overloaded versions of the AddServiceEndpoint( ) method:
public class ServiceHost : ServiceHostBase
{
   public ServiceEndpoint AddServiceEndpoint(Type implementedContract,
                                             Binding binding,
                                             string address);
   //Additional members
}

You can provide AddServiceEndpoint( ) methods with either relative or absolute addresses, just as with a config file. Example 1-9 demonstrates programmatic configuration of the same endpoints as in Example 1-7.
Example 1-9. Service-side programmatic endpoint configuration
ServiceHost host = new ServiceHost(typeof(MyService));
 
Binding wsBinding  = new WSHttpBinding( );
Binding tcpBinding = new NetTcpBinding( );
 
host.AddServiceEndpoint(typeof(IMyContract),wsBinding,
                        "http://localhost:8000/MyService");
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,
                        "net.tcp://localhost:8001/MyService");
host.AddServiceEndpoint(typeof(IMyOtherContract),tcpBinding,
                        "net.tcp://localhost:8002/MyService");
 
host.Open( );

When you add an endpoint programmatically, the address is given as a string, the contract as a Type, and the binding as one of the subclasses of the abstract class Binding, such as:
public class NetTcpBinding : Binding,...
{...}

To rely on the host base address, provide an empty string if you want to use the base address, or just the URI to use the base address plus the URI:
Uri tcpBaseAddress = new Uri("net.tcp://localhost:8000/");
 
ServiceHost host = new ServiceHost(typeof(MyService),tcpBaseAddress);
 
Binding tcpBinding = new NetTcpBinding( );
 
//Use base address as address
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,"");
//Add relative address
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,"MyService");
//Ignore base address
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,
                               "net.tcp://localhost:8001/MyService");
host.Open( );

As with administrative configuration using a config file, the host must provide a matching base address; otherwise, an exception occurs. In fact, there is no difference between programmatic and administrative configuration. When you use a config file, all WCF does is parse the file and execute the appropriate programmatic calls in its place.
1.7.2.1. Binding configuration
You can programmatically set the properties of the binding used. For example, here is the code required to enable transaction propagation similar to Example 1-8:
ServiceHost host = new ServiceHost(typeof(MyService));
 
NetTcpBinding tcpBinding = new NetTcpBinding( );
 
tcpBinding.TransactionFlow = true;
 
host.AddServiceEndpoint(typeof(IMyContract),tcpBinding,
                        "net.tcp://localhost:8000/MyService");
host.Open( );

Note that when you're dealing with specific binding properties, you typically interact with a concrete binding subclass such as NetTcpBinding, and not its abstract base clas

Data Representation

The development of a Web service with ASP.NET typically begins with defining any complex data types the service is to use. ASP.NET relies on the XmlSerializer to translate data represented by .NET Framework types to XML for transmission to or from a service and to translate data received as XML into .NET Framework objects. Defining the complex data types that an ASP.NET service is to use requires the definition of .NET Framework classes that the XmlSerializer can serialize to and from XML. Such classes can be written manually, or generated from definitions of the types in XML Schema using the command-line XML Schemas/Data Types Support Utility, xsd.exe.
The following is a list of key issues to know when defining .NET Framework classes that the XmlSerializer can serialize to and from XML:
  • Only the public fields and properties of .NET Framework objects are translated into XML.
  • Instances of collection classes can be serialized into XML only if the classes implement either the IEnumerable or ICollection interface.
  • Classes that implement the IDictionary interface, such as Hashtable, cannot be serialized into XML.
  • The great many attribute types in the System.Xml.Serialization namespace can be added to a .NET Framework class and its members to control how instances of the class are represented in XML.
WCF application development usually also begins with the definition of complex types. WCF can be made to use the same .NET Framework types as ASP.NET Web services.
The WCF DataContractAttribute and DataMemberAttribute can be added to .NET Framework types to indicate that instances of the type are to be serialized into XML, and which particular fields or properties of the type are to be serialized, as shown in the following sample code.

//Example One: 
[DataContract]
public class LineItem
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}
 
//Example Two: 
public class LineItem
{
    [DataMember]
    private string itemNumber;
    [DataMember]
    private decimal quantity;
    [DataMember]
    private decimal unitPrice;
 
    public string ItemNumber
    {
      get
      {
          return this.itemNumber;
      }
 
      set
      {
          this.itemNumber = value;
      }
    }
 
    public decimal Quantity
    {
        get
        {
            return this.quantity;
        }
 
        set
        {
            this.quantity = value;
        }
    }
 
    public decimal UnitPrice
    {
      get
      {
          return this.unitPrice;
      }
 
      set
      {
          this.unitPrice = value;
      }
    }
}
 
//Example Three: 
public class LineItem
{
     private string itemNumber;
     private decimal quantity;
     private decimal unitPrice;
 
     [DataMember]
     public string ItemNumber
     {
       get
       {
          return this.itemNumber;
       }
 
       set
       {
           this.itemNumber = value;
       }
     }
 
     [DataMember]
     public decimal Quantity
     {
          get
          {
              return this.quantity;
          }
 
          set
          {
             this.quantity = value;
          }
     }
 
     [DataMember]
     public decimal UnitPrice
     {
          get
          {
              return this.unitPrice;
          }
 
          set
          {
              this.unitPrice = value;
          }
     }
}
The DataContractAttribute signifies that zero or more of a type’s fields or properties are to be serialized, while the DataMemberAttribute indicates that a particular field or property is to be serialized. The DataContractAttribute can be applied to a class or structure. The DataMemberAttribute can be applied to a field or a property, and the fields and properties to which the attribute is applied can be either public or private. Instances of types that have the DataContractAttribute applied to them are referred to as data contracts in WCF. They are serialized into XML using DataContractSerializer.
The following is a list of the important differences between using the DataContractSerializer and using the XmlSerializer and the various attributes of the System.Xml.Serialization namespace.
  • The XmlSerializer and the attributes of the System.Xml.Serialization namespace are designed to allow you to map .NET Framework types to any valid type defined in XML Schema, and so they provide for very precise control over how a type is represented in XML. The DataContractSerializer, DataContractAttribute and DataMemberAttribute provide very little control over how a type is represented in XML. You can only specify the namespaces and names used to represent the type and its fields or properties in the XML, and the sequence in which the fields and properties appear in the XML:
[DataContract(
Namespace="urn:Contoso:2006:January:29",
Name="LineItem")]
public class LineItem
{
     [DataMember(Name="ItemNumber",IsRequired=true,Order=0)]
     public string itemNumber;
     [DataMember(Name="Quantity",IsRequired=false,Order = 1)]
     public decimal quantity;
     [DataMember(Name="Price",IsRequired=false,Order = 2)]
     public decimal unitPrice;
}
Everything else about the structure of the XML used to represent the .NET type is determined by the DataContractSerializer.
  • By not permitting much control over how a type is to be represented in XML, the serialization process becomes highly predictable for the DataContractSerializer, and, thereby, easier to optimize. A practical benefit of the design of the DataContractSerializer is better performance, approximately ten percent better performance.
  • The attributes for use with the XmlSerializer do not indicate which fields or properties of the type are serialized into XML, whereas the DataMemberAttribute for use with the DataContractSerializer shows explicitly which fields or properties are serialized. Therefore, data contracts are explicit contracts about the structure of the data that an application is to send and receive.
  • The XmlSerializer can only translate the public members of a .NET object into XML, the DataContractSerializer can translate the members of objects into XML regardless of the access modifiers of those members.
  • As a consequence of being able to serialize the non-public members of types into XML, the DataContractSerializer has fewer restrictions on the variety of .NET types that it can serialize into XML. In particular, it can translate into XML types like Hashtable that implement the IDictionary interface. The DataContractSerializer is much more likely to be able to serialize the instances of any pre-existing .NET type into XML without having to either modify the definition of the type or develop a wrapper for it.
  • Another consequence of the DataContractSerializer being able to access the non-public members of a type is that it requires full trust, whereas the XmlSerializer does not. The Full Trust code access permission give complete access to all resources on a machine that can be access using the credentials under which the code is executing. This options should be used with care as fully trusted code accesses all resources on your machine.
  • The DataContractSerializer incorporates some support for versioning:
    • The DataMemberAttribute has an IsRequired property that can be assigned a value of false for members that are added to new versions of a data contract that were not present in earlier versions, thereby allowing applications with the newer version of the contract to be able to process earlier versions.
    • By having a data contract implement the IExtensibleDataObject interface, one can allow the DataContractSerializer to pass members defined in newer versions of a data contract through applications with earlier versions of the contract.
Despite all of the differences, the XML into which the XmlSerializer serializes a type by default is semantically identical to the XML into which the DataContractSerializer serializes a type, provided the namespace for the XML is explicitly defined. The following class, which has attributes for use with both of the serializers, are translated into semantically identical XML by the XmlSerializer and by the DataContractAttribute:

[Serializable]
[XmlRoot(Namespace="urn:Contoso:2006:January:29")]
[DataContract(Namespace="urn:Contoso:2006:January:29")]
public class LineItem
{
     [DataMember]
     public string ItemNumber;
     [DataMember]
     public decimal Quantity;
     [DataMember]
     public decimal UnitPrice;
}
The binding specifies the set of protocols for communicating with the application. The following table lists the system-provided bindings that represent common options.
Name
Purpose
BasicHttpBinding
Interoperability with Web services and clients supporting the WS-BasicProfile 1.1 and Basic Security Profile 1.0.
WSHttpBinding
Interoperability with Web services and clients that support the WS-* protocols over HTTP.
WSDualHttpBinding
Duplex HTTP communication, by which the receiver of an initial message does not reply directly to the initial sender, but may transmit any number of responses over a period of time by using HTTP in conformity with WS-* protocols.
WSFederationBinding
HTTP communication, in which access to the resources of a service can be controlled based on credentials issued by an explicitly-identified credential provider.
NetTcpBinding
Secure, reliable, high-performance communication between WCF software entities across a network.
NetNamedPipeBinding
Secure, reliable, high-performance communication between WCF software entities on the same machine.
NetMsmqBinding
Communication between WCF software entities by using MSMQ.
MsmqIntegrationBinding
Communication between a WCF software entity and another software entity by using MSMQ.
NetPeerTcpBinding
Communication between WCF software entities by using Windows Peer-to-Peer Networking.
The system-provided binding, BasicHttpBinding, incorporates the set of protocols supported by ASP.NET Web services.
Custom bindings for WCF applications are easily defined as collections of the binding element classes that WCF uses to implement individual protocols. New binding elements can be written to represent additional protocols.
The internal behavior of service types can be adjusted using the properties of a family of classes called behaviors. Here, the ServiceBehaviorAttribute class is used to specify that the service type is to be multithreaded.

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple]
public class DerivativesCalculatorServiceType: IDerivativesCalculator
Some behaviors, like ServiceBehaviorAttribute, are attributes. Others, the ones with properties that administrators would want to set, can be modified in the configuration of an application.
In programming service types, frequent use is made of the OperationContext class. Its static Current property provides access to information about the context in which an operation is running. OperationContext is similar to both the HttpContext and ContextUtil classes.

Hosting

ASP.NET Web services are compiled into a class library assembly. A file called the service file is provided that has the extension .asmx and contains an @ WebService directive that identifies the class that contains the code for the service and the assembly in which it is located.

<%@ WebService Language="C#" Class="Service,ServiceAssembly" %>
The service file is copied into an ASP.NET application root in Internet Information Services (IIS), and the assembly is copied into the \bin subdirectory of that application root. The application is then accessible by using the uniform resource locator (URL) of the service file in the application root.
WCF services can readily be hosted within IIS 5.1 or 6.0, the Windows Process Activation Service (WAS) that is provided as part of IIS 7.0, and within any .NET application. To host a service in IIS 5.1 or 6.0, the service must use HTTP as the communications transport protocol.
To host a service within IIS 5.1, 6.0 or within WAS, use the follows steps:
1.        Compile the service type into a class library assembly.
2.        Create a service file with a .svc extension with an @ ServiceHost directive to identify the service type:

<%@ServiceHost language=”c#” Service="MyService" %>
3.        Copy the service file into a virtual directory, and the assembly into the \bin subdirectory of that virtual directory.
4.        Copy the configuration file into the virtual directory, and name it Web.config.
The application is then accessible by using the URL of the service file in the application root.
To host a WCF service within a .NET application, compile the service type into a class library assembly referenced by the application, and program the application to host the service using the ServiceHost class. The following is an example of the basic programming required:

string httpBaseAddress = "http://www.contoso.com:8000/";
string tcpBaseAddress = "net.tcp://www.contoso.com:8080/";
 
Uri httpBaseAddressUri = new Uri(httpBaseAddress);
Uri tcpBaseAddressUri = new Uri(tcpBaseAddress);
 
Uri[] baseAdresses = new Uri[] { 
 httpBaseAddressUri,
 tcpBaseAddressUri};
 
using(ServiceHost host = new ServiceHost(
typeof(Service), //”Service” is the name of the service type baseAdresses))
{
     host.Open();
 
     […] //Wait to receive messages
     host.Close();
}
This example shows how addresses for one or more transport protocols are specified in the construction of a ServiceHost. These addresses are referred to as base addresses.
The address provided for any endpoint of a WCF service is an address relative to a base address of the endpoint’s host. The host can have one base address for each communication transport protocol. The base address of an endpoint is relative to whichever of the base addresses of the host is the base address for the communication transport protocol of the endpoint. In the sample configuration in the preceding configuration file, the BasicHttpBinding selected for the endpoint uses HTTP as the transport protocol, so the address of the endpoint, EchoService, is relative to the host’s HTTP base address. In the case of the host in the preceding example, the HTTP base address is http://www.contoso.com:8000/. For a service hosted within IIS or WAS, the base address is the URL of the service’s service file.
Only services hosted in IIS or WAS, and which are configured with HTTP as the transport protocol exclusively, can be made to use WCF ASP.NET compatibility mode option. Turning that option on requires the following steps.
1.        The programmer must add the AspNetCompatibilityRequirementsAttribute attribute to the service type and specify that ASP.NET compatibility mode is either allowed or required.

[System.ServiceModel.Activation.AspNetCompatibilityRequirements(
      RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
public class DerivativesCalculatorServiceType: IDerivativesCalculator
2.        The administrator must configure the application to use the ASP.NET compatibility mode.

<configuration>
     <system.serviceModel>
      <services>
      […]
      </services>
      <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    </system.serviceModel>
</configuration>
WCF applications can also be configured to use .asmx as the extension for their service files rather than .svc.
<system.web>
     <compilation>
      <compilation debug="true">
      <buildProviders>
       <remove extension=".asmx"/>
       <add extension=".asmx" 
        type="System.ServiceModel.ServiceBuildProvider, 
        Systemm.ServiceModel, 
        Version=3.0.0.0, 
        Culture=neutral, 
        PublicKeyToken=b77a5c561934e089" />
      </buildProviders>
      </compilation>
     </compilation>
</system.web>
That option can save you from having to modify clients that are configured to use the URLs of .asmx service files when modifying a service to use WCF.

Client Development

Clients for ASP.NET Web services are generated using the command-line tool, WSDL.exe, which provides the URL of the .asmx file as input. The corresponding tool provided by WCF is Service Model Metadata Tool (Svcutil.exe). It generates a code module with the definition of the service contract and the definition of a WCF client class. It also generates a configuration file with the address and binding of the service.
In programming a client of a remote service it is generally advisable to program according to an asynchronous pattern. The code generated by the WSDL.exe tool always provides for both a synchronous and an asynchronous pattern by default. The code generated by the Service Model Metadata Tool (svcutil.exe) can provide for either pattern. It provides for the synchronous pattern by default. If the tool is executed with the /async switch, then the generated code provides for the asynchronous pattern.
There is no guarantee that names in the WCF client classes generated by ASP.NET’s WSDL.exe tool, by default, match the names in WCF client classes generated by the Svcutil.exe tool. In particular, the names of the properties of classes that have to be serialized using the XmlSerializer are, by default, given the suffix Property in the code generated by the Svcutil.exe tool, which is not the case with the WSDL.exe tool.

Message Representation

The headers of the SOAP messages sent and received by ASP.NET Web services can be customized. A class is derived from SoapHeader to define the structure of the header, and then the SoapHeaderAttribute is used to indicate the presence of the header.

public class SomeProtocol : SoapHeader
{
     public long CurrentValue;
     public long Total;
}
 
[WebService]
public interface IEcho
{
     SomeProtocol ProtocolHeader
     {
      get;
     set;
     }
 
     [WebMethod]
     [SoapHeader("ProtocolHeader")]
     string PlaceOrders(PurchaseOrderType order);
}
 
public class Service: WebService, IEcho
{
     private SomeProtocol protocolHeader;
 
     public SomeProtocol ProtocolHeader
     {
         get
         {
              return this.protocolHeader;
         }
 
         set
         {
              this.protocolHeader = value;
         }
     }
 
     string PlaceOrders(PurchaseOrderType order)
     {
         long currentValue = this.protocolHeader.CurrentValue;
     }
}
The WCF provides the attributes, MessageContractAttribute, MessageHeaderAttribute, and MessageBodyMemberAttribute to describe the structure of the SOAP messages sent and received by a service.

[DataContract]
public class SomeProtocol
{
     [DataMember]
     public long CurrentValue;
     [DataMember]
     public long Total;
}
 
[DataContract]
public class Item
{
     [DataMember]
     public string ItemNumber;
     [DataMember]
     public decimal Quantity;
     [DataMember]
     public decimal UnitPrice;
}
 
[MessageContract]
public class ItemMesage
{
     [MessageHeader]
     public SomeProtocol ProtocolHeader;
     [MessageBody]
     public Item Content;
}
 
[ServiceContract]
public interface IItemService
{
     [OperationContract]
     public void DeliverItem(ItemMessage itemMessage);
}
This syntax yields an explicit representation of the structure of the messages, whereas the structure of messages is implied by the code of an ASP.NET Web service. Also, in the ASP.NET syntax, message headers are represented as properties of the service, such as the ProtocolHeader property in the previous example, whereas in WCF syntax, they are more accurately represented as properties of messages. Also, WCF allows message headers to be added to the configuration of endpoints.

<service name="Service ">
     <endpoint 
      address="EchoService"
      binding="basicHttpBinding"
      contract="IEchoService ">
      <headers>
      <dsig:X509Certificate 
       xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
       ...
      </dsig:X509Certificate>
      </headers>
     </endpoint>
</service>
That option allows you to avoid any reference to infrastructural protocol headers in the code for a client or service: the headers are added to messages because of how the endpoint is configured.

No comments:

Post a Comment