The Future of ASP.NET Web Services in the Context of the Windows Communication Foundation

 

Craig McMurtry
Technical Evangelist
Microsoft Corporation

July 2006

Applies to:
   Microsoft ASP.NET 2.0
   Windows Communication Foundation
   Internet Information Services (IIS)
   Web services specifications

Contents

The Decision Maker's Perspective
Comparing the Technologies: Purpose
Comparing the Technologies: Standards
Comparing the Technologies: Development
Internal Architecture
Anticipating Adopting the Windows Communication Foundation: Easing Future Integration
Anticipating Adopting the Windows Communication Foundation: Easing Future Migration
Adopting the Windows Communication Foundation
Summary

Summary: This article compares ASP.NET Web services to the Windows Communication Foundation and explains what to do about existing and planned ASP.NET Web services now that the Windows Communication Foundation is soon to be released. (47 printed pages)

ASP.NET provides .NET Framework class libraries and tools for building Web services, as well as facilities for hosting those services within Internet Information Services (IIS). The Windows Communication Foundation, which was code-named Indigo, provides .NET class libraries, tools and hosting facilities for enabling software entities to communicate using any protocols, including those used by Web services. So, what are the prospects for ASP.NET Web services now that a newer, more general technology for building Web services is coming?

This paper compares the two technologies. It describes how ASP.NET Web service applications can be used together with Windows Communication Foundation applications. The paper also explains both how to prepare for migrating ASP.NET Web services to the Windows Communication Foundation, and how to actually do that migration.

For the purposes of this paper, the facilities that ASP.NET provides for building Web services are considered separately from the Web Services Enhancements for Microsoft .NET (WSE). The prospects for applications developed using WSE will be examined elsewhere.

The Decision Maker's Perspective

The Windows Communication Foundation is scheduled to be released in the second half of 2006. It has several important advantages relative to ASP.NET Web services that any organization using that earlier technology needs to consider.

Whereas the ASP.NET Web services tools are solely for building Web services, the Windows Communication Foundation provides tools for use in any circumstance where software entities must be made to communicate with one another. That should reduce the number of technologies that developers are required to know in order to accommodate different software communication scenarios, which should in turn reduce the cost of software development resources, as well as the time to complete software development projects.

Even for Web service development projects, the Windows Communication Foundation supports more Web service protocols than ASP.NET Web services support. Those protocols provide for more sophisticated solutions involving, amongst other things, reliable sessions and transactions.

The Windows Communication Foundation supports more protocols for transporting messages than the ASP.NET Web services support. ASP.NET Web services only support sending messages via the Hypertext Transfer Protocol (HTTP). The Windows Communication Foundation supports sending messages via HTTP, as well as the Transmission Control Protocol (TCP), named pipes, and Microsoft Message Queuing (MSMQ). More important, the Windows Communication Foundation can be readily extended to support additional transport protocols. Therefore, software developed using the Windows Communication Foundation can be readily adapted to work together with a wider variety of other software, thereby increasing the potential return on the investment in it.

The Windows Communication Foundation provides much richer facilities for deploying and managing applications than ASP.NET Web services provides. In addition to a configuration system, which ASP.NET also has, the Windows Communication Foundation offers a configuration editor, activity tracing from senders to receivers and back via any number of intermediaries, a trace viewer, message logging, a vast number of performance counters, and support for Windows Management Instrumentation. This rich set of administrative tools should serve to reduce operational costs, the risk of failures, and actual downtime.

Given these potential benefits of the Windows Communication Foundation relative to ASP.NET Web services, organizations that are using, or are considering using ASP.NET Web services have several options:

  • Simply continue to use ASP.NET Web services, and forego the benefits proffered by the Windows Communication Foundation. Under Microsoft's current support lifecycle policy, mainstream support for ASP.NET Web services will be provided until at least the year 2011, and extended support will be provided until at least 2016. So, there is no great risk in continuing to use ASP.NET Web services.
  • Keep using ASP.NET Web services with the intention of adopting the Windows Communication Foundation at some time in the future. This paper will explain how to maximize the prospects for being able to use new ASP.NET Web service applications together with future Windows Communication Foundation applications. It will also explain how to build new ASP.NET Web services so as to make it easier to migrate them to the Windows Communication Foundation. However, if securing the services is important, or reliability or transaction assurances are required, or if it appears that custom management facilities will have to be constructed, then the decision to defer adopting the Windows Communication Foundation is probably mistaken. It is designed for precisely such scenarios, and production licenses for the technology are already available.
  • Adopt the Windows Communication Foundation for new development, while continuing to maintain their existing ASP.NET Web service applications. This choice is very likely the optimal one. It yields the benefits of the Windows Communication Foundation, while sparing the cost of modifying the existing applications to use it. In this scenario, new Windows Communication Foundation applications can co-exist with existing ASP.NET applications. Also, new Windows Communication Foundation applications will be able to use existing ASP.NET Web services, and the Windows Communication Foundation can even be used to program new operational capabilities into existing ASP.NET applications by virtue of the Windows Communication Foundation's ASP.NET compatibility mode.
  • Adopt the Windows Communication Foundation and migrate existing ASP.NET Web service applications to the Windows Communication Foundation. An organization might choose this option in order to eliminate any requirement for their developers to know about ASP.NET Web services. That would not be a good reason for this choice, though, because the prospect of eliminating support for any technologies other than the latest ones is probably chimerical. The only good reasons for migrating would be to enhance the existing applications with features provided by the Windows Communication Foundation, or to reproduce the functionality of existing ASP.NET Web services within new, more powerful Windows Communication Foundation applications. This paper will explain how to accomplish the migration.

Comparing the Technologies: Purpose

The ASP.NET Web services technology was developed for building applications that send and receive messages via the Simple Object Access Protocol (SOAP) over HTTP. The structure of the messages can be defined using XML Schema, and a tool is provided to facilitate serializing the messages to and from .NET 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.

The Windows Communication Foundation is for enabling .NET applications to exchange messages with other software entities. SOAP is used by default, but the messages can be in any format, and conveyed via any transport protocol. The structure of the messages can be defined using XML Schema, and there are various options for serializing the messages to and from .NET objects. The Windows Communication Foundation 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.

Comparing the Technologies: Standards

For a list of the standards supported by ASP.NET Web services, see XML Web Services Created Using ASP.NET.

See Web Services Protocols Supported in WCF for a more extensive list of standards supported by the Windows Communication Foundation.

Comparing the Technologies: Development

ASP.NET Compatibility Mode

The Windows Communication Foundation has an ASP.NET compatibility mode option by which certain Windows Communication Foundation applications can be programmed and configured like ASP.NET Web services, and will mimic their behavior. How to opt for ASP.NET compatibility mode is explained below, and details of the exact effects of that option will be provided.

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 System.Xml.Serialization.XmlSerializer ** to translate data represented by .NET objects into XML for transmission to or from a service, and to translate data received as XML into .NET objects. So, defining the complex data types that an ASP.NET service is to use requires the definition of .NET classes that the System.Xml.Serialization.XmlSerializer can serialize to and from XML. Such classes can be written manually, of course, or generated from definitions of the types in XML Schema using the command-line XML Schemas/Data Types Support Utility, xsd.exe.

These are the key things to know about defining .NET classes that the System.Xml.Serialization.XmlSerializer will be able to serialize to and from XML:

  • Only the public fields and properties of .NET objects will be translated into XML.
  • Instances of collection classes can be serialized into XML only if the classes implement either the IEnumerable or ICollection interface.
  • As a corollary, classes that implement the System.Collections.IDictionary interface, like System.Collections.Hashtable, cannot be serialized into XML.
  • The great many attribute types in the System.Xml.Serialization namespace can be added to a .NET class and its members to control exactly how instances of the class are represented in XML.

Windows Communication Foundation application development usually also begins with the definition of complex types. The Windows Communication Foundation can be made to use the same .NET types as ASP.NET Web services. It offers a better alternative, though.

The System.Runtime.Serialization.DataContract and System.Runtime.Serialization.DataMember attributes of the Windows Communication Foundation's System.Runtime.Serialization assembly can be added to .NET 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. All three of the following examples are valid:

//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 System.Runtime.Serialization.DataContract attribute signifies that zero or more of a type's fields or properties are to be serialized, while the System.Runtime.Serialization.DataMember attribute indicates that a particular field or property is to be serialized. The System.Runtime.Serialization.DataContract attribute can be applied to a class or struct. The System.Runtime.Serialization.DataMember attribute 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 System.Runtime.Serialization.DataContract attributes are referred to as data contracts in the argot of the Windows Communication Foundation. They are serialized into XML using the Windows Communication Foundation's System.Runtime.Serialization.DataContractFormatter.

How do the System.Runtime.Serialization.DataContractFormatter and the System.Runtime.Serialization.DataContract and System.Runtime.Serialization.DataMember attributes differ from the System.Xml.Serialization.XmlSerializer and the various attributes of the System.Xml.Serialization namespace? There are many important differences.

  1. The System.Xml.Serialization.XmlSerializer and the attributes of the System.Xml.Serialization namespace are designed to allow one to map .NET types to any valid type defined in XML Schema, and so they provide for very precise control over how a .NET type is represented in XML. The System.Runtime.Serialization.DataContractFormatter and the System.Runtime.Serialization.DataContract and System.Runtime.Serialization.DataMember attributes provide very little control over how a .NET type is represented in XML. One 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:Woodgrove: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 System.Runtime.Serialization.DataContractFormatter.

  2. By not permitting much control over how a .NET type is to be represented in XML, the serialization process becomes highly predictable for the System.Runtime.Serialization.DataContractFormatter, and, thereby, more amenable to optimization. So, a practical benefit of the design of the System.Runtime.Serialization.DataContractFormatter is better performance, approximately ten percent better performance.

  3. Compare the following type, which has attributes for serialization with the System.Xml.Serialization.XmlSerializer,

    [Serializable]
    [XmlRoot(Namespace="urn:Woodgrove:2006:January:29")]
    public class LineItem
    {
        public string ItemNumber;
        public decimal Quantity;
        public decimal UnitPrice;
    } 
    

    with this version, which has attributes for use with the System.Runtime.Serialization.DataContractFormatter:

    [DataContract(Namespace="urn:Woodgrove:2006:January:29")]
    public class LineItem
    {
        [DataMember]
        public string ItemNumber;
        [DataMember]
        public decimal Quantity;
        [DataMember]
        public decimal UnitPrice;
    } 
    

    The attributes for use with the System.Xml.Serialization.XmlSerializer do not indicate which fields or properties of the type will be serialized into XML, whereas the System.Runtime.Serialization.DataMember attribute for use with the System.Runtime.Serialization.DataContractFormatter shows explicitly which fields or properties will be serialized. Therefore, one can say that data contracts are explicit contracts about the structure of the data that an application is to send and receive.

  4. Whereas the System.Xml.Serialization.XmlSerializer can only translate the public members of a .NET object into XML, the System.Runtime.Serialization.DataContractFormatter can translate the members of .NET objects into XML regardless of the access modifiers of those members.

  5. Partly as a consequence of being able to serialize the non-public members of types into XML, the System.Runtime.Serialization.DataContractFormatter has fewer restrictions on the variety of .NET types that it can serialize into XML. In particular, it can translate into XML types like System.Collections.Hashtable that implement the Systems.Collections.IDictionary interface. Generally, the System.Runtime.Serialization.DataContractFormatter is much more likely to be able to serialize the instances of any pre-existing .NET type into XML without one having to either modify the definition of the type or develop a wrapper for it.

  6. However, another consequence of the System.Runtime.Serialization.DataContractFormatter being able to access the non-public members of a type is that it requires full trust, whereas the System.Xml.Serialization.XmlSerializer does not.

  7. The System.Runtime.Serialization.DataContractFormatter incorporates some support for versioning.

    • The System.Runtime.Serialization.DataMember attribute 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 simple System.Runtime.Serialization.IExtensibleDataObject interface, one can allow the System.Runtime.Serialization.DataContractFormatter to pass members defined in newer versions of a data contract through applications with earlier versions of the contract.

Despite all of those differences, the XML into which the System.Xml.Serialization.XmlSerializer will serialize a type by default is semantically identical to the XML into which the System.RuntimeSerialization.DataContractFormatter will serialize a type, provided the namespace for the XML is explicitly defined. So, this class, which has attributes for use with both of the serializers, will be translated into semantically identical XML by the System.Xml.Serialization.XmlSerializer and by the System.RuntimeSerialization.DataContractFormatter:

[Serializable]
[XmlRoot(Namespace="urn:Woodgrove:2006:January:29")]
[DataContract(Namespace="urn:Woodgrove:2006:January:29")]
public class LineItem
{
    [DataMember]
    public string ItemNumber;
    [DataMember]
    public decimal Quantity;
    [DataMember]
    public decimal UnitPrice;
}

The software development kit that accompanies the Windows Communication Foundation includes a command-line tool called the Service Model Metadata Tool, svcutil.exe. Like the xsd.exe tool used with ASP.NET Web services, svcutil.exe can generate definitions of .NET data types from XML Schema. Those .NET data types will be data contracts if the System.Runtime.Serialization.DataContractFormatter can emit XML in the format defined by the XML Schema; otherwise, they will be intended for serialization using the System.Xml.Serialization.XmlSerializer. The tool, svcutil.exe, can also be made to generate XML Schema from data contracts using its /dataContractOnly switch.

Note that although ASP.NET Web services use the System.Xml.Serialization.XmlSerializer, and the Windows Communication Foundation's ASP.NET compatibility mode makes Windows Communication Foundation services mimic the behavior of ASP.NET Web services, the ASP.NET compatibility option does not restrict one to using the System.Xml.Serialization.XmlSerializer. One can still use the System.Runtime.Serialization.DataContractFormatter with services running in the ASP.NET compatibility mode.

Service Development

To develop a service using ASP.NET, it has been customary to simply add the System.Web.Services.WebService attribute to a class, and the System.Web.Services.WebMethod attribute to any of that class' methods that are to be operations of the service:

[WebService]
public class Service : System.Web.Services.WebService
{
    [WebMethod]
    public string Echo(string input) 
    {
        return input;
    }
}

ASP.NET 2.0 introduced the option of adding the System.Web.Services.WebService and System.Web.Services.WebMethod attributes to an interface rather than to a class, and writing a class to implement the interface:

[WebService]
public interface IEcho
{
    [WebMethod]
    string Echo(string input);
}

public class Service : IEcho
{

    public string Echo(string input)
    {
        return input;
    }
}

Using this option is to be preferred, because the interface with the System.Web.Services.WebService attribute constitutes a contract for the operations performed by the service that can be reused with various classes that might implement that same contract in different ways.

A Windows Communication Foundation service is provided by defining one or more Windows Communication Foundation endpoints. An endpoint is defined by an address, a binding and a service contract. The address defines where the service is located. The binding specifies how to communicate with the service. The service contract defines the operations that the service can perform.

The service contract is usually defined first, by adding System.ServiceModel.ServiceContract and System.ServiceModel.OperationContract attributes to a .NET interface:

[ServiceContract]
public interface IEcho
{
    [OperationContract]
    string Echo(string input);
}

The System.ServiceModel.ServiceContract attribute specifies that the interface defines a Windows Communication Foundation service contract, and the System.ServiceModel.OperationContract attribute indicates which, if any, of the methods of the interface define operations of the service contract.

Once a service contract has been defined, it is implemented in a class, simply by having the class implement the interface by which the service contract is defined:

public class Service : IEcho
{
    
    public string Echo(string input)
    {
        return input;
    }
}

A class that implements a service contract is referred to as a service type in the argot of the Windows Communication Foundation.

The next step is to associate an address and a binding with a service type. That is typically done in a configuration file, either by editing the file directly, or by using a configuration editor provided with the Windows Communication Foundation. Here is an example of a configuration file.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <system.serviceModel>
      <services>
         <service name="Service ">
            <endpoint 
               address="EchoService"
               binding="basicHttpBinding"
               contract="IEchoService "/>
         </service>
      </services>
   </system.serviceModel>
</configuration>

The binding specifies the set of protocols for communicating with the application. There are a number of predefined 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 supporting the WS-* protocols over HTTP
WSDualHttpBinding Duplex HTTP communication, by which the receiver of an initial message will not reply directly to the initial sender, but may transmit any number of responses over a period of time via 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 Windows Communication Foundation software entities across a network
NetNamedPipeBinding Secure, reliable, high-performance communication between Windows Communication Foundation software entities on the same machine
NetMsmqBinding Communication between Windows Communication Foundation software entities via MSMQ
MsmqIntegrationBinding Communication between a Windows Communication Foundation software entity and another software entity via MSMQ
NetPeerTcpBinding Communication between Windows Communication Foundation software entities via Windows Peer-to-Peer Networking

The predefined binding, System.ServiceModel.BasicHttpBinding, incorporates the set of protocols supported by ASP.NET Web services.

Custom bindings for Windows Communication Foundation applications are easily defined as collections of the binding element classes that the Windows Communication Foundation 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 System.ServiceModel.ServiceBehavior class is used to specify that the service type is to be multithreaded:

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple]
public class DerivativesCalculatorServiceType: IDerivativesCalculator

Some behaviors, the ones with properties that programmers would want to set, like System.ServiceModel.ServiceBehavior, 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 System.ServiceModel.OperationContext class. Its static Current property provides access to information about the context in which an operation is executing. Thus, System.ServiceModel.OperationContext is similar to both the System.Web.HttpContext and System.EnterpriseServices.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 identifying the class containing 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 IIS, and the assembly is copied into the \bin subdirectory of that application root. The application is then accessible via the uniform resource locator (URL) of the service file in the application root.

Aaron Skonnard has explained how to use the HttpListener class provided by the .NET Framework 2.0 to host ASP.NET Web services outside of IIS, in any .NET application. However, Skonnard himself describes the effort involved as "non-trivial."

Windows Communication Foundation services can readily be hosted within IIS 5.1 or 6.0, the Windows Activation Service (WAS) that is to be provided as part of IIS 7, 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 or 6.0, or within WAS one follows these 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 via the URL of the service file in the application root.

To host a Windows Communication Foundation 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 Windows Communication Foundation's System.ServiceModel.ServiceHost class. Here is an example of the simple programming required:

string httpBaseAddress = "http://www.woodgrove.com:8000/";
string tcpBaseAddress = "net.tcp://www.woodgrove.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 Windows Communication Foundation System.ServiceModel.ServiceHost. These addresses are referred to as base addresses.

The address provided for any endpoint of a Windows Communication Foundation service is an address relative to a base address of the endpoint's host. The host can have one base address per communication transport protocol. The 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 example of a configuration file shown above, the System.ServiceModel.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 example above, the HTTP base address is http://www.woodgrove.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 the Windows Communication Foundation's ASP.NET compatibility mode option. Turning that option on requires two steps.

  1. The programmer must add the System.ServiceModel.AspNetCompatbilityRequirements attribute to the service type, specifying that ASP.NET compatibility mode is either allowed or required:

    [System.ServiceModel.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>
    

Windows Communication Foundation 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 one from having to modify clients that have been configured to use the URLs of .asmx service files when modifying a service to use the Windows Communication Foundation.

Client Development

Clients for ASP.NET Web services are generated using the command-line tool, wsdl.exe, providing the URL of the .asmx file as input. The corresponding tool provided by the Windows Communication Foundation is svcutil.exe. It generates a code module with the definition of the service contract and the definition of a proxy 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 svcutil.exe tool 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 proxy classes generated by ASP.NET's wsdl.exe tool will, by default, match the names in proxy classes generated by the Windows Communication Foundation's svcutil.exe tool. In particular, the names of the properties of classes that have to be serialized using the System.Xml.Serialization.XmlSerializer are, by default, given the suffix Property in the code generated by the svcutil.exe tool, which was 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 System.Web.Services.Protocols to define the structure of the header, and then the System.Web.Services.SoapHeader attribute 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 Windows Communication Foundation provides the attributes, System.ServiceModel.MessageContract, System.ServiceModel.MessageHeader, and System.ServiceModel.MessageBody 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 merely 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 example above, whereas in the Windows Communication Foundation syntax, they are more accurately represented as properties of messages. Also, the Windows Communication Foundation 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 one to avoid any reference to infrastructural protocol headers in the code for a client or service: the headers get added to messages simply by virtue of how the endpoint is configured.

Service Description

Issuing an HTTP GET request for the .asmx file of an ASP.NET Web service with the query wsdl causes ASP.NET to generate WSDL to describe the service. It will return that WSDL as the response to the request.

ASP.NET 2.0 made it possible to validate that a service is compliant with the Basic Profile 1.1 of the Web Services-Interoperability Organization (WS-I), and to insert a claim that the service is compliant into its WSDL. That is done using the ConformsTo and EmitConformanceClaims parameters of the System.Web.Services.WebServiceBinding attribute:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
    ConformsTo = WsiProfiles.BasicProfile1_1,
    EmitConformanceClaims=true)]
public interface IEcho

The WSDL that ASP.NET generates for a service can be customized. Customizations are made by creating a subclass of System.Web.Services.Description.ServiceDescriptionFormatExtension to add items to the WSDL.

Issuing an HTTP GET request with the query wsdl for the .svc file of a Windows Communication Foundation service with an HTTP endpoint hosted within IIS 51, 6.0 or WAS will cause the Windows Communication Foundation to respond with WSDL to describe the service. Issuing an HTTP GET request with the query wsdl to the HTTP base address of a service hosted within a .NET application will have the same effect.

However, the Windows Communication Foundation will also respond to WS-MetadataExchange requests with WSDL that it generates to describe a service. ASP.NET Web services do not have built-in support for WS-MetadataExchange requests.

The WSDL that the Windows Communication Foundation generates can be extensively customized. The System.ServiceModel.ServiceMetadataBehavior class provides some facilities for customizing the WSDL, and one can develop implementations of System.ServiceModel.Design.IWsdlExporter to take complete control. The Windows Communication Foundation can also be configured to not generate WSDL, but rather to use a static WSDL file at a given URL:

<behaviors>
   <serviceBehaviors>
      <behavior name="DescriptionBehavior">
        <metadataPublishing 
        enableMetadataExchange="true" 
        enableGetWsdl="true" 
        enableHelpPage="true" 
        metadataLocation=
        "https://localhost/DerivativesCalculatorService/Service.wsdl"/>
      </behavior>
   </serviceBehaviors>
</behaviors>

Exception Handling

In ASP.NET Web services, unhandled exceptions are returned to clients as SOAP faults. One can also explicitly throw instances of the System.Web.Services.Protocols.SoapException class, and thereby have more control over the content of the SOAP fault that gets transmitted to the client.

In Windows Communication Foundation services, unhandled exceptions are not returned to clients as SOAP faults to prevent sensitive information being inadvertently exposed through the exceptions. A configuration setting is provided to have unhandled exceptions returned to clients for the purpose of debugging.

To deliberately return SOAP faults to clients, Windows Communication Foundation programmers can throw instances of the generic type, System.ServiceModel.FaultException<T>, where T should be a data contract. The programmers can also add System.ServiceModel.FaultContract attributes to operations to specify the faults that an operation might yield:

[DataContract]
public class MathFault
{    
    [DataMember]
    public string operation;
    [DataMember]
    public string problemType;
}

[ServiceContract]
public interface ICalculator
{
    [OperationContract]
    [FaultContract(typeof(MathFault))]
    int Divide(int n1, int n2);
}

Doing so will result in the possible faults being advertised in the WSDL for the service, allowing client programmers to anticipate precisely which faults could result from an operation, and write the appropriate catch statements:

try
{
    result = proxy.Divide(value1, value2);
}
catch (FaultException<MathFault> e)
{
    Console.WriteLine("FaultException<MathFault>: Math fault while doing " 
      + e.Detail.operation 
      + ". Problem: " 
      + e.Detail.problemType);
}

State Management

The class used to implement an ASP.NET Web service may be derived from System.Web.Services.WebService:

public class Service : WebService, IEcho
{

    public string Echo(string input)
    {
        return input;
    }
}

In that case, the class can be programmed to use the System.Web.Services.WebService base class' Context property to access a System.Web.HttpContext object. The System.Web.HttpContext object can be used to update and retrieve application state information via its Application property, and can be used to update and retrieve session state information via its Session property.

ASP.NET provides considerable control over where the session state information accessed via the Session property of the System.Web.HttpContext is actually stored. It may be stored in cookies, in a database, in the memory of the current server, or in the memory of a designated server. The choice is made in the service's configuration file.

The Windows Communication Foundation provides extensible objects for state management. Extensible objects are objects that implement System.ServiceModel.IExtensibleObject<T>. The most important extensible objects are System.ServiceModel.ServiceHostBase and System.ServiceModel.InstanceContext. The former allows one to maintain state that all of the instances of all of the service types on the same host can access, while the latter allows one to maintain state that can be accessed by any code executing within the same instance of a service type.

Here, the service type, TradingSystem, has a System.ServiceModel.ServiceBehavior attribute that specifies that all calls from the same proxy instance will be routed to the same instance of the service type.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class TradingSystem: ITradingService

The class, DealData, defines state that can be accessed by any code executing in the same instance of a service type:

internal class DealData: IExtension<InstanceContext>
{
    public string DealIdentifier = null;
    public Trade[] Trades = null;
}

In the code of the service type that implements one of the operations of the service contract, a DealData state object is added to the state of the current instance of the service type:

string ITradingService.BeginDeal()
{
    string dealIdentifier = Guid.NewGuid().ToString();
    DealData state = new DealData(dealIdentifier);
    OperationContext.Current.InstanceContext.Extensions.Add(state);
    return dealIdentifier;
}

That state object can then be retrieved and modified by the code implementing another of the service contract's operations:

void ITradingService.AddTrade(Trade trade)
{
    DealData dealData =      OperationContext.Current.InstanceContext.Extensions.Find<DealData>();
    dealData.AddTrade(trade);
}

Whereas ASP.NET provides considerable control over where state information in the System.Web.HttpContext class is actually stored, the Windows Communication Foundation, at least in its initial version, provides no control over where extensible objects are stored. That constitutes the very best reason for selecting the ASP.NET compatibility mode for a Windows Communication Foundation service. If configurarable state mangement is imperative, then opting for the ASP.NET compatibility mode will allow one to use the facilities of the System.Web.HttpContext class exactly as they are used in ASP.NET, and also to configure where state information managed via the System.Web.HttpContext class is stored.

Security

The options for securing ASP.NET Web services are mostly just those for securing any IIS application. Because Windows Communication Foundation applications can be hosted not only within IIS but also within any .NET executable, the options for securing Windows Communication Foundation applications had to be made independent from the facilities of IIS. However, the facilities provided for ASP.NET Web services are also available for Windows Communication Foundation services running in ASP.NET compatibility mode.

Security: Authentication

IIS provides facilities for controlling access to applications by which one can select either anonymous access or a variety of modes of authentication: Windows Authentication, Digest Authentication, Basic Authentication, and .NET Passport Authentication. The Windows Authentication option can be used to control access to ASP.NET Web services. However, when Windows Communication Foundation applications are hosted within IIS, IIS must be configured to permit anonymous access to the application, so that authentication can be managed by the Windows Communication Foundation itself, which does support Windows authentication among various other options. The other options that are built-in include username tokens, X.509 certificates, SAML tokens, and InfoCard, but custom authentication mechanisms can also be defined.

In ASP.NET 2.0, the System.Web.HttpContext class has a Profile property by which information about the authenticated user can be automatically retrieved from a store via a System.Web.Profile.Provider class that knows how to read that information from a particular kind of store. This ASP.NET 2.0 mechanism is not supported in the Windows Communication Foundation except in ASP.NET compatibility mode, although a custom behavior could certainly use System.Web.Profile.Provider classes to retrieve profile information.

Security: Impersonation

ASP.NET provides an identity element by which an ASP.NET Web service can be made to impersonate a particular user or whichever user's credentials were provided with the current request. That element can be used to configure impersonation in Windows Communication Foundation applications running in ASP.NET compatibility mode.

The Windows Communication Foundation's configuration system provides its own identity element for designating a particular user to impersonate. Also, Windows Communication Foundation clients and services can be independently configured for impersonation. Clients can be configured to impersonate the current user when they transmit requests:

<behaviors>
   <endpointBehaviors>
      <behavior name="DerivativesCalculatorClientBehavior">
         <clientCredentials>
            <windows allowedImpersonationLevel="Impersonation"/>
         </clientCredentials>
      </behavior>
   </endpointBehaviors>
</behaviors>

Service operations can be configured to impersonate whichever user's credentials were provided with the current request:

[OperationBehavior(Impersonation = ImpersonationOption.Required)]
public void Receive(Message input)

Security: Authorization using Access Control Lists

Access Control Lists (ACLs) can be used to restrict access to .asmx files. However, ACLs on Windows Communication Foundation .svc files are ignored except in ASP.NET compatibility mode.

Security: Role-based Authorization

The IIS Windows Authentication option can be used in conjunction with the authorization element provided by the ASP.NET configuration language to facilitate role-based authorization for ASP.NET Web services based on the Windows groups to which users are assigned. ASP.NET 2.0 introduced a more general role-based authorization mechanism: role providers.

Role providers are classes that all implement a simple interface for enquiring about the roles to which a user is assigned, but each role provider knows how to retrieve that information from a different source. ASP.NET 2.0 provides a Role Provider that can retrieve role assignments from a Microsoft SQL Server database, and another that can retrieve role assignments from the Windows Server 2003 Authorization Manager.

The role provider mechanism can actually be used independently of ASP.NET in any .NET application, including a Windows Communication Foundation application. Here is a sample configuration for a Windows Communication Foundation application that shows how the use of an ASP.NET Role Provider is an option selected by means of the System.ServiceModel.ServiceAuthorization behavior:

<system.serviceModel>
  <services>
    <service name="Service.ResourceAccessServiceType" 
     behaviorConfiguration="ServiceBehavior">
     <endpoint 
       address="ResourceAccessService" 
      binding="wsHttpBinding" 
      contract="Service.IResourceAccessContract"/>
    </service>
  </services>
  <behaviors>
   <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceAuthorization principalPermissionMode="UseAspNetRoles"/>
        </behavior>
      </serviceBehaviors>
   </behaviors>
</system.serviceModel>

Security: Claims-based Authorization

One of the most important innovations of the Windows Communication Foundation is its thorough support for authorizing access to protected resources based on claims. Claims consist of a type, a right and a value. Consider a drivers' license, for example. It makes a set of claims about the bearer, one of which is the bearer's date of birth. The type of that claim is date of birth, while the value of the claim is the driver's birth date. The right that a claim confers on the bearer specifies what the bearer can do with the claim's value. In the case of the claim of the driver's date of birth, the right is simply possession: the driver possesses that date of birth but cannot, for example, alter it. Claims-based authorization subsumes role-based authorization, because roles are simply a type of claim.

Authorization based on claims is accomplished by comparing a set of claims to the access requirements of the operation, and, depending on the outcome of that comparison, granting or denying access to the operation. In the Windows Communication Foundation, one can specify a class to use to execute claims-based authorization, once again by assigning a value to the ServiceAuthorizationManager property of System.ServiceModel.Description.ServiceAuthorizationBehavior:

<behaviors>
  <serviceBehaviors>
    <behavior name='ServiceBehavior'>
   <serviceAuthorization 
        serviceAuthorizationManagerType='Service.AccessChecker, Service' />
    </behavior>
  </serviceBehaviors>
</behaviors>

Classes used to execute claims-based authorization must derive from System.ServiceModel.ServiceAuthorizationManager, which has just one method to override, AccessCheck(). The Windows Communication Foundation calls that method whenever an operation of the service is invoked, providing a System.ServiceModel.OperationContext object, which has the claims for the user in its ServiceSecurityContext.AuthorizationContext property. Thus, the Windows Communication Foundation will have already done the work of assembling claims about the user from whatever security token the user provided for authentication, leaving the simple of task of evaluating whether those claims suffice for the operation in question.

That the Windows Communication Foundation will automatically assemble claims from any kind of security token is a highly significant innovation, because it makes the code for authorization based on the claims entirely independent of the authentication mechanism. By contrast, authorization using ACLs or roles in ASP.NET is closely tied to Windows authentication.

Security: Confidentiality

The confidentiality of messages exchanged with ASP.NET Web services can be ensured at the transport level by configuring the application within IIS to use the Secure Hypertext Transfer Protocol (HTTPS). The same can be done for Windows Communication Foundation applications hosted within IIS. However, Windows Communication Foundation applications hosted outside of IIS can also be configured to use a secure transport protocol. More important, Windows Communication Foundation applications can also be configured to secure the messages before they are transported, using the WS-Security protocol. Securing just the body of a message using WS-Security allows it to be transmitted confidentially across intermediaries before reaching its final destination.

Globalization

The ASP.NET configuration language allows one to specify the culture for individual services. The Windows Communication Foundation does not support that configuration setting except in ASP.NET compatibility mode. To localize a Windows Communication Foundation service that does not use ASP.NET compatibility mode, one would compile the service type into culture-specific assemblies, and have separate culture-specific endpoints for each culture-specific assembly.

Internal Architecture

ASP.NET Web Services

In the most recent implementation of ASP.NET, an HTTP request is received in the form of a System.Web.HttpWorkerRequest object from an implementation of HTTP in the Windows kernel that is called http.sys. The System.Web.HttpWorkerRequest object is received by a System.Web.HttpRuntime object that populates the System.Web.HttpContext object from the data in the System.Web.HttpWorkerRequest object. The request is incorporated in the System.Web.HttpContext object as its Request property, which is a System.Web.HttpRequest object.

By default, a System.Web.HttpRequest object that represents a HTTP POST request addressed to an .asmx file is passed to an object that implements the System.Web.IHttpHandler interface (Skonnard 2003, 2004). That object is created using the System.Web.Services.Protocols.WebServiceHandlerFactory class, and may be referred to as the ASP.NET Web service request handler. When the Windows Communication Foundation is configured for ASP.NET compatibility mode, it provides an implementation of the System.Web.IHttpHandler that mimics the behavior of the ASP.NET Web service request handler.

The ASP.NET Web service request handler must do at least four things. First, it must pass any SOAP message incorporated in the HTTP request to instances of any SOAP E\extensions provided by the developer (Meier, Vasireddy, Babbar, and Mackman 2004). Second, it has to determine which method of the class referred to in the .asmx file is to be invoked to process the request. Third, it must have the request de-serialized into instances of the types that the method expects as parameters. Fourth, it has to actually invoke the method, passing it the parameters it has de-serialized from the request.

SOAP extensions are types derived from System.Web.Services.Protocols.SoapExtension. They are used to access or modify SOAP messages in requests before the messages are passed on to any method of the class referred to in the .asmx file for processing. They can also access and modify response messages, and can be deployed not only on servers but also on clients. A common use for SOAP extensions is to log messages. Another is for encrypting them. SOAP extensions can be applied to all of the services deployed on a machine, or to individual services, or, by using a custom System.Web.Services.SoapExtension attribute, to particular operations of a service.

To determine which method of a class to invoke to process a request, the ASP.NET Web service request handler uses, by default, the SOAPAction header in the request. By default, the request handler expects the SOAPAction HTTP header to consist of the namespace of the service followed by the name of the operation. The default namespace of a service is http://tempuri.org/, and the default name for an operation is the name of the method that defines it. So, in processing this HTTP request,

POST /asmxservice/service.asmx HTTP/1.1
User-Agent: Mozilla/4.0 
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/Echo"
Host: localhost
Content-Length: 314
Expect: 100-continue
Connection: Keep-Alive

<?xml version="1.0" encoding="UTF-8" ?> 
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<Echo xmlns="http://tempuri.org/">
<input>Hello, World
</input> 
</Echo>
</soap:Body>
</soap:Envelope>

the request handler expects that the class referred to by the @_WebService directive in the file located at https://localhost/asmxservice/service.asmx defines a service with the default namespace that has an operation called Echo. It also expects, by default, that the operation is implemented by a method of the class that is called Echo, and the handler will attempt to invoke that method to process the request. All of this behavior can be customized:

One can have the request handler not use the SOAPAction HTTP header, but rather use the fully-qualified element name of the body element of the SOAP message to identify which method to invoke. In the foregoing example of an HTTP request, the body element is,

<Echo xmlns="http://tempuri.org/">
[...]
</soap:Body>

and its fully-qualified element name is http://tempuri.org/Echo. Since that name is the same as the SOAPAction HTTP header, it should be apparent how the ASP.NET Web service request handler would use the name to determine how to route the message. To have the request handler use the fully-qualified name of the body element of the SOAP message rather than the SOAPAction HTTP header, one applies the System.Web.Services.Protocols.SoapDocumentService attribute to the class that implements the service, and specifies RequestElement as the routing style, thus:

[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
public class Service : WebService, IEcho

Also, one can use the RequestElementName parameter of the System.Web.Services.Protocols.SoapDocumentMethod attribute to make the local name of the body element of the SOAP message different from the name of the method:

[WebMethod]
[SoapDocumentMethod(RequestElementName="OtherName")]
string Echo(string input);

Then the ASP.NET Web service request handler will match the local name of the body element of the SOAP message to the value provided for the RequestElementName parameter of the System.Web.Services.Protocols.SoapDocumentMethod attribute applied to the method, rather than matching the local name of the body element directly to the name of the method.

The namespace of the service can be modified from the default using the Namespace parameter of the System.Web.Services.WebService attribute:

[WebService(Namespace = "http://www.woodgrove.com/2006/01/29/")]
public interface IEcho

Although the value of this parameter is shown here being modified for a System.Web.Services.WebService attribute applied to an interface, regrettably, due to a bug, altering the value of the parameter actually has no effect when the attribute is applied to an interface rather than a class.

The name of an operation can be made different from the name of the method that implements it. That is done by specifying a value for the MessageName parameter of the System.Web.Services.WebMethod attribute:

[WebMethod(MessageName="OtherName")]
string Echo(string input);

This facility can be used to distinguish polymorphic methods for the ASP.NET Web service request handler:

[WebMethod(MessageName="OtherName")]
string Echo(string input);
[WebMethod]
string[] Echo(string[] inputs);

One can add the System.Web.Services.Protocols.SoapDocumentMethod attribute to a method and provide a value for the Action parameter of the attribute, like so:

[WebMethod]
[SoapDocumentMethod(Action="urn:echoing:echo")]
string Echo(string input);

For any such method, the ASP.NET Web service request handler will simply match the SOAPAction HTTP header to the value specified for the Action parameter of the SoapDocumentMethod attribute, rather than attempting to identify the target method by decomposing the SOAPAction header into the namespace of the service together and the operation name.

Once the ASP.NET Web service request handler has identified which method of the class referred to in the .asmx file is to be invoked to process the request, the request must be de-serialized into instances of the types the method expects as parameters. It uses the System.Xml.Serialization.XmlSerializer to do that.

By default, the request handler assumes that the SOAP message incorporated in the request conforms to what the WSDL 1.1 specification calls the document style. A message in that style has the elements of its body structured in accordance with arbitrary schemas, and the request handler relies on the System.Xml.Serialization.XmlSerializer to de-serialize those elements into .NET types. In the case of this operation,

[WebMethod]
PurchaseOrderConfirmationType PlaceOrders(PurchaseOrderType order); 

the ASP.NET Web service request handler would anticipate document-style SOAP messages like this one,

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:s1="urn:Woodgrove:2006:January:29" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
xmlns:tns="http://tempuri.org/" 
xmlns:xs="http://www.w3.org/2001/XMLSchema">
   <soap:Body>
      <tns:PlaceOrders>
         <s1:PurchaseOrder>
            <s1:Date>2006-01-31</s1:Date>
            <s1:LineItems>
               <s1:LineItem>
                  <s1:ItemNumber>1</s1:ItemNumber>
                  <s1:Quantity>1</s1:Quantity>
                  <s1:UnitPrice>50.00</s1:UnitPrice>
               </s1:LineItem>
            </s1:LineItems>
            <s1:Total>50.00</s1:Total>
         </s1:PurchaseOrder>
      </tns:PlaceOrders>
   </soap:Body>
</soap:Envelope>

wherein the names of the elements identify the names of the types into which they are to be de-serialized. This mechanism can also be customized:

  1. How the names of types are mapped to constituents of the SOAP message can be customized by applying the attributes, System.Xml.XmlElement and System.Xml.XmlAttribute, to the parameters of a method. The System.Xml.XmlElement attribute controls the name of the XML element that the ASP.NET Web service request handler will expect to de-serialize a type:

    [WebMethod]
    PurchaseOrderType PlaceOrders
    (
        [XmlElement("OrderParameter")]
        PurchaseOrderType order
    );
    

    If the attribute, System.Xml.XmlAttribute, has been added to a parameter, then the request handler will be expecting to deserialize that parameter from an XML attribute rather than an XML element.

  2. One can alter the ASP.NET Web service request handler's default expectation that the SOAP message incorporated in the request conforms to the document style, and instead have it expect the message to be in what the WSDL 1.1 specification refers to as the RPC style. That can be done for an entire service using the SoapRpcService attribute, or for individual methods using the SoapRpcMethod attribute. Use of these options is not recommended, however, because using the RPC style is not included in the WS-I Basic Profile 1.1, and thereby diminishes the prospects for interoperability.

Windows Communication Foundation

The proxy class for a service used in a Windows Communication Foundation client serializes parameters passed to any of its methods into a System.ServiceModel.Channels.Message object. That object is then passed through a series of channels, the number and nature of which are determined by the selected binding. Those channels will typically add System.ServiceModel.Channels.MessageHeader objects to the Headers collection of the System.ServiceModel.Channels.Message object in accordance with a protocol that the channel implements. The last channel in the series is always a transport channel. That channel will use an encoder, the nature of which is also determined by the binding, to serialize the System.ServiceModel.Channels.Message object into a stream of bytes which the transport channel transmits to the server. A listener on the server receives the stream of bytes and uses an encoder to de-serialize the stream of bytes into a System.ServiceModel.Channels.Message object. That object is then passed through a series of channels that will usually correspond to the series of channels on the client. Then the System.ServiceModel.Channels.Message object is passed to a System.ServiceModel.Dispatcher.DispatchRuntime object. The System.ServiceModel.Dispatcher.DispatchRuntime object determines which method of the service type is to be invoked, de-serializes data from the System.ServiceModel.Channels.Message object into instances of the types the method expects as parameters, and invokes the method.

Not only can the selection and operation of the channels through which data transmissions pass within the Windows Communication Foundation be customized via the binding of a service, custom channels can readily be added. Also, the operation of any proxy and the System.ServiceModel.Dispatcher.DispatchRuntime can be adjusted or entirely customized via the behaviors. Thus, whereas ASP.NET provides a number of attributes for controlling its Web service request handler, the Windows Communication Foundation not only offers a similar set of attributes, but also the option of swapping in custom code to control a proxy and the System.ServiceModel.Dispatcher.DispatchRuntime.

In determining which method of a service type is to be invoked to process a request, the System.ServiceModel.Dispatcher.DispatchRuntime relies on the SOAPAction header. By default, the SOAPAction header for a Windows Communication Foundation operation consists of the namespace of the service, followed by the name of the service contract, followed by the name of the operation. The default namespace for a service contract is http://tempuri.org/. The default name for a service contract is the name of the interface or class used to define it, and the default name of an operation is the name of the method that implements it. So, in the case of this operation,

[ServiceContract]
public interface IDerivativesCalculator
{
    [OperationContract]
    decimal CalculateDerivative(
        string[] symbols,
        decimal[] parameters,
        string[] functions);

}

the SOAPAction header will be http://tempuri.org/IDerivativesCalculator/CalculateDerivative. Namespaces and names for service contracts can be altered from their defaults using the Namespace and Name parameters of the System.ServiceModel.ServiceContract attribute, and the names of operations can be altered from their defaults using the Name parameter of the System.ServiceModel.OperationContract attribute:

[ServiceContract(Namespace="OtherNamespace",Name="OtherContractName"]
public interface IDerivativesCalculator
{
    [OperationContract(Name="OtherOperationName")]
    decimal CalculateDerivative(
        string[] symbols,
        decimal[] parameters,
        string[] functions);
}

In de-serializing data from a System.ServiceModel.Message object, the System.ServiceModel.Dispatcher.DispatchRuntime uses the System.Runtime.Serialization.DataContractFormatter by default. The mechanism of the Namespace and Name parameters of the System.ServiceModel.DataContract and System.ServiceModel.DataMember attributes for matching the names of XML elements to the classes into which they are to be de-serialized has already been mentioned.

The System.ServiceModel.Dispatcher.DispatchRuntime can be made to use the System.Xml.Serialization.XmlSerializer by applying the System.ServiceModel.XmlSerializerFormat attribute:

[ServiceContract, XmlSerializerFormat]
public interface IEcho

That attribute can also be applied to the individual operations of a service.

If the System.Runtime.Serialization.DataContractFormatter is being used for de-serialization, then whether the data is expected to be in document style or the RPC style can be controlled using the System.ServiceModel.DataContractFormat attribute. That attribute can be applied either to a service contract, or to individual operations of a service contract:

[ServiceContract]
public interface IItemService
{
    [OperationContract]
    [DataContractFormat(Style=OperationFormatStyle.Rpc)]
    public void DeliverItem(ItemMessage itemMessage);
}

Anticipating Adopting the Windows Communication Foundation: Easing Future Integration

If one uses ASP.NET today, and anticipates using the Windows Communication Foundation in future, then here is guidance to follow to ensure that new ASP.NET Web services will work well together with Windows Communication Foundation applications.

General Recommendations

Adopt ASP.NET 2.0 for any new services. Doing so will of course provide access to the improvements and enhancements of the new version. However, it will also allow for the possibility of using ASP.NET 2.0 components together with Windows Communication Foundation components in the same application.

Protocols

Use ASP.NET 2.0's new facility for validating conformity to the WS-I Basic Profile 1.1:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(
    ConformsTo = WsiProfiles.BasicProfile1_1,
    EmitConformanceClaims=true)]
public interface IEcho

ASP.NET Web services that conform to that profile are more interoperable generally, and will certainly be interoperable with Windows Communication Foundation clients via the Windows Communication Foundation's predefined binding, System.ServiceModel.BasicHttpBinding.

Service Development

Avoid using the System.Web.Services.Protocols.SoapDocumentService attribute to have messages dispatched to methods based on the fully-qualified name of the body element of the SOAP message rather than the SOAPAction HTTP header. The Windows Communication Foundation uses the SOAPAction HTTP header for dispatching messages to methods.

Data Representation

Recall that the XML into which the System.Xml.Serialization.XmlSerializer will serialize a type by default is semantically identical to the XML into which the System.RuntimeSerialization.DataContractFormatter will serialize a type, provided the namespace for the XML is explicitly defined. So, in defining a data type for use with ASP.NET Web services today while anticipating adopting the Windows Communication Foundation in the future, do the following:

  1. Define the type using .NET classes rather than XML Schema.
  2. Add only the System.Serializable attribute and the System.Xml.Serialization.XmlRoot attribute to the class, using the latter to explicitly define the namespace for the type. Refrain from adding additional attributes from the System.Xml.Serialization namespace to control how the .NET class is to be translated into XML.

By adopting this approach, one should be able to later make the .NET classes into data contracts with the addition of the System.Runtime.Serialization.DataContract and System.Runtime.Serialization.DataMember attributes without significantly altering the XML into which those classes are serialized for transmission. Then the same types used in messages by ASP.NET Web services will be able to be processed as data contracts by Windows Communication Foundation applications, yielding, amongst other benefits, better performance in the Windows Communication Foundation applications.

Security

Avoid using IIS' authentication options. Windows Communication Foundation clients do not support them. If a service needs to be secured, then one should adopt the Windows Communication Foundation right away, because it has richer options based on standard protocols.

Anticipating Adopting the Windows Communication Foundation: Easing Future Migration

To ensure an easier future migration of new ASP.NET applications to the Windows Communication Foundation, follow the preceding recommendations as well as these:

Protocols

Disable ASP.NET 2.0 support for SOAP 1.2:

<configuration>
    <system.web>
        <webServices >
            <protocols>
                <remove name="HttpSoap12"/>
            </protocols>      
        </webServices>
    </system.web>   
</configuration>

Doing so is advisable because the Windows Communication Foundation requires messages conforming to different protocols, like SOAP 1.1 and SOAP 1.2, to go via different endpoints. If an ASP.NET 2.0 Web service is configured to support both SOAP 1.1 and SOAP 1.2, which is the default configuration, then it could not be migrated forward to a single Windows Communication Foundation endpoint at the original address that would be certainly be compatible with all of the ASP.NET Web service's existing clients.

Service Development

  • The Windows Communication Foundation allows one to define service contracts by applying the System.ServiceModel.ServiceContract attribute either to interfaces or to classes. It is recommended to apply the attribute to an interface rather than to a class, because doing yields a definition of a contract that can be variously implemented by any number of classes. ASP.NET 2.0 supports the option of applying the System.Web.Services.WebService attribute to interfaces as well as classes. However, as mentioned already, there is a defect in ASP.NET 2.0, by which the Namespace parameter of the System.Web.Services.WebService attribute has no effect when that attribute is applied to an interface rather than a class. Since it is generally generally advisable to modify the namespace of a service from the default value, http://tempuri.org, using the Namespace parameter of the System.Web.Services.WebService attribute, one should continue defining ASP.NET Web Services by applying the System.ServiceModel.ServiceContract attribute either to interfaces or to classes.

  • Have as little code as possible in the methods by which those interfaces are defined. Have them delegate their work to other classes. New Windows Communication Foundation service types could then also simply delegate their substantive work to those classes.

  • Provide explicit names for the operations of a service using the MessageName parameter of the System.Web.Services.WebMethod attribute.

    [WebMethod(MessageName="ExplicitName")]
    string Echo(string input);
    

    Doing so is important, because the default names for operations in ASP.NET are different from the default names supplied by the Windows Communication Foundation. By providing explicit names, one avoids relying on the default ones.

  • Do not implement ASP.NET Web service operations with polymorphic methods, because the Windows Communication Foundation does not support implementing operations with polymorphic methods.

  • Use the System.Web.Services.Protocols.SoapDocumentMethod attribute to provide explicit values for the SOAPAction HTTP headers by which HTTP requests will be routed to methods.

    [WebMethod]
    [SoapDocumentMethod(RequestElementName="ExplicitAction")]
    string Echo(string input);
    
  • Again, taking this approach will circumvent having to rely on the default SOAPAction values used by ASP.NET and the Windows Communication Foundation being the same.

  • Avoid using SOAP extensions. If SOAP extensions are required, then determine whether the purpose for which they are being considered is a feature that is already provided by the Windows Communication Foundation. If that is indeed the case, then reconsider the choice to not adopt the Windows Communication Foundation right away.

State Management

Avoid having to maintain state in services. Not only does maintaining state tend to compromise the scalability of an application, but the state management mechanisms of ASP.NET and the Windows Communication Foundation are very different, although the Windows Communication Foundation does support the ASP.NET mechanisms in ASP.NET compatibility mode.

Exception Handling

In designing the structures of the data types to be sent and received by a service, also design structures to represent the various types of exceptions that might occur within a service that one might wish to convey to a client.

[Serializable]
[XmlRoot(
    Namespace="ExplicitNamespace", IsNullable=true)]
public partial class AnticipatedException {
    
    private string anticipatedExceptionInformationField;
    
    public string AnticipatedExceptionInformation {
        get {
            return this.anticipatedExceptionInformationField;
        }
        set {
            this.anticipatedExceptionInformationField = value;
        }
    }

}

Give those classes the ability to serialize themselves to XML:

public XmlNode ToXML()
{
    XmlSerializer serializer = new XmlSerializer(
        typeof(AnticipatedException));
    MemoryStream memoryStream = new MemoryStream();
    XmlTextWriter writer = new XmlTextWriter(
        memoryStream, UnicodeEncoding.UTF8);
    serializer.Serialize(writer, this);
    XmlDocument document = new XmlDocument();
    document.LoadXml(new string(
        UnicodeEncoding.UTF8.GetChars(
memoryStream.GetBuffer())).Trim());
    return document.DocumentElement;
}

Those classes can then be used to provide the details for explicitly thrown System.Web.Services.Protocols.SoapException instances:

AnctipatedException exception = new AnticipatedException();
exception.AnticipatedExceptionInformation = "...";
throw new SoapException(
   "Fault occurred",
   SoapException.ClientFaultCode,
   Context.Request.Url.AbsoluteUri,
   exception.ToXML());

These exception classes will be readily reusable with the Windows Communication Foundation's System.ServiceModel.FaultContract<T>:

throw new FaultException<AnticipatedException>(anticipatedException);

Security

  • Avoid using ASP.NET 2.0 Profiles.
  • Avoid using ACLs to control access to services.
  • Do consider using ASP.NET 2.0 Role Providers for authorizing access to the resources of a service.

Adopting the Windows Communication Foundation

Co-existence with ASP.NET Web Services

One can opt to use the Windows Communication Foundation for new development, while continuing to maintain existing applications developed using ASP.NET. Because the Windows Communication Foundation is intended to be the most suitable choice for facilitating communication with .NET applications in any scenario, it can indeed serve as a standard tool for solving a wide variety of software communications problems in a way that ASP.NET never could.

New Windows Communication Foundation applications can be deployed on the same machines as existing ASP.NET Web services. If those ASP.NET Web services use a version of .NET prior to version 2.0, then one would use the ASP.NET IIS Registration Tool to selectively deploy the .NET Framework 2.0 to IIS applications in which new Windows Communication Foundation applications are to be hosted. See the documentation for ASP.NET IIS Registration Tool (Aspnet_regiis.exe). The tool has an intuitive user interface built into the IIS 6 management console.

The Windows Communication Foundation can be used to add new features to existing ASP.NET Web services by adding Windows Communication Foundation services configured to run in ASP.NET compatibility mode to existing ASP.NET Web service applications in IIS. By virtue of ASP.NET compatibility mode, the code for the new Windows Communication Foundation services will actually be able to access and update the same application state information as the pre-existing ASP.NET code, via the System.Web.HttpContext class. They will also be able to share the same class libraries.

Migrating ASP.NET Web Services and Clients to the Windows Communication Foundation

Windows Communication Foundation clients can use ASP.NET Web services. Windows Communication Foundation services that are configured with the System.ServiceModel.BasicHttpBinding can be used by ASP.NET Web service clients. ASP.NET Web services can co-exist with Windows Communication Foundation applications, and the Windows Communication Foundation can even be used to add features to existing ASP.NET Web services. Given all of these ways in which the Windows Communication Foundation and ASP.NET Web services can be used together, there are few compelling reasons for migrating ASP.NET Web services to the Windows Communication Foundation.

Even in the few cases where it is deemed to be necessary, carefully consider that simply migrating code from one technology to another is seldom the right approach. The reason for adopting the new technology would be to meet new requirements that could not be met with the earlier technology, and, in that case, the right thing to do is to design a new solution to meet the newly-expanded set of requirements. The new design would benefit from one's experience with the existing system and from wisdom gained since that system was designed. The new design would also take into account the full capabilities of the new technologies rather than simply reproducing the old design on the new platform. After prototyping key elements of the new design, it generally becomes quite obvious how to re-use code from the existing system within the new one.

For the few cases where a simple port from ASP.NET Web services to the Windows Communication Foundation is deemed to be the right solution, some guidance follows on how to proceed. There is advice on how to migrate services, and how to migrate clients.

Migrating an ASP.NET Web Service to the Windows Communication Foundation

  1. Ensure that a comprehensive suite of tests exist for the service.

  2. Generate the WSDL for the service, and save a copy in the same folder as the service's .asmx file.

  3. Upgrade the ASP.NET Web service to use .NET 2.0. Do so by first deploying the .NET Framework 2.0 to the application in IIS, and then by using Visual Studio 2005 to automate the code conversion process, as documented here: https://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/webprojectsVS05.asp?frame=true. Execute the suite of tests.

  4. Provide explicit values for the Namespace and Name parameters of the System.Web.Services.WebService attributes if they are not provided already. Do the same for the MessageName parameter of the System.Web.Services.WebMethod attributes. Also, if explicit values are not already provided for the SOAPAction HTTP headers by which requests are routed to methods, then explicitly specify the default value of the Action parameter with a System.Web.Services.Protocols.SoapDocumentMethod attribute:

    [WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
    public class Adder
    {
        [WebMethod(MessageName = "Add")]
        [SoapDocumentMethod(Action = "http://tempuri.org/Add")]
        public double Add(SumInput input)
        {
            double sum = 0.00;
            foreach (double inputValue in input.Input)
            {
                sum += inputValue;
            }
            return sum;
        }
    }
    
  5. Execute the suite of tests.

  6. Move any substantive code in the bodies of the methods of the class to a separate class that the original class is made to use.

    [WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
    public class Adder
    {
        [WebMethod(MessageName = "Add")]
        [SoapDocumentMethod(Action = "http://tempuri.org/Add")]
        public double Add(SumInput input)
        {
            return new ActualAdder().Add(input);
        }
    }
    
    internal class ActualAdder
    {
        internal double Add(SumInput input)
        {
            double sum = 0.00;
            foreach (double inputValue in input.Input)
            {
                sum += inputValue;
            }
            return sum;
        }
    }
    
  7. Execute the suite of tests.

  8. Add references to the Windows Communication Foundation assemblies System.ServiceModel and System.Runtime.Serialization to the ASP.NET Web service project.

  9. Execute the Windows Communication Foundation's svcutil.exe tool to generate a Windows Communication Foundation proxy class from the WSDL. Add the generated class module to the solution.

  10. The class module generated in the preceding step will contain the definition of an interface.

    [System.ServiceModel.ServiceContractAttribute()]
    public interface AdderSoap
    {
        [System.ServiceModel.OperationContractAttribute(
          Action="http://tempuri.org/Add", 
          ReplyAction="http://tempuri.org/Add")]
        AddResponse Add(AddRequest request);
    }
    Modify the definition of the ASP.NET Web service class so that the class is defined as implementing that interface: 
    [WebService(Namespace = "http://tempuri.org/", Name = "Adder")]
    public class Adder: AdderSoap
    {
        [WebMethod(MessageName = "Add")]
        [SoapDocumentMethod(Action = "http://tempuri.org/Add")]
        public double Add(SumInput input)
        {
            return new ActualAdder().Add(input);
        }
    
    
        public AddResponse Add(AddRequest request)
        {
            return new AddResponse(
    new AddResponseBody(
    this.Add(request.Body.input)));
        }
    }
    
  11. Compile the project. There may be some errors due to the code generated in step nine duplicating some type definitions. Repair those errors, usually by deleting the pre-existing definitions of the types. Execute the suite of tests.

  12. Remove the ASP.NET-specific attributes, such as the System.Web.Services.WebService, System.Web.Services.WebMethod, and System.Web.Services.Protocols.SoapDocumentMethod attributes:

    public class Adder: AdderSoap
    {
        public double Add(SumInput input)
        {
            return new ActualAdder().Add(input);
        }
    
    
        public AddResponse Add(AddRequest request)
        {
            return new AddResponse(
    new AddResponseBody(
    this.Add(request.Body.input)));
        }
    }
    
  13. Configure the class, which will now be a Windows Communication Foundation service type, to require the Windows Communication Foundation's ASP.NET compatibility mode if the ASP.NET Web service relied on any of the following:

    • the System.Web.Services.HttpContext class
    • the ASP.NET Profiles
    • ACLs on .asmx files
    • IIS authentication options
    • ASP.NET impersonation options
    • ASP.NET globalization
    [System.ServiceModel.AspNetCompatibilityRequirements(
           RequirementsMode=AspNetCompatbilityRequirementsMode.Require)]
    public class Adder: AdderSoap
    
  14. Rename the original .asmx file to .asmx.old.

  15. Create a Windows Communication Foundation service file for the service, give it the extension, .asmx, and save it into the application root in IIS.

    <%@Service Class="MyOrganization.Adder" %>
    <%@Assembly Name="MyServiceAssembly" %>  
    
  16. Add a Windows Communication Foundation configuration for the service to its Web.config file. Configure the service to use the BasicHttpBinding, to use the service file with the .asmx extension created in the preceding steps, and to not generate WSDL for itself, but rather to use the WSDL from step two, above. Also configure it to use ASP.NET compatibility mode if that was determined to be necessary in step ten, above.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
       <system.web>
          <compilation>
             <buildProviders>
    <remove extension=".asmx" />
                <add extension=".asmx" 
                   type=
    "System.ServiceModel.ServiceBuildProvider,             System.ServiceModel, Version=2.0.0.0, 
                Culture=neutral, 
                PublicKeyToken=b77a5c561934e089" />
             </buildProviders>
          </compilation>
       </system.web>
       <system.serviceModel>
          <services>
             <service name="MyOrganization.Adder "
                      behaviorConfiguration="AdderBehavior">
    <endpoint 
    address=""
    binding="basicHttpBinding"
    contract="AdderSoap "/>
             </service>
          </services>
          <behaviors>
               <serviceBehaviors>
             <behavior name="AdderBehavior">
                <metadataPublishing 
                   enableMetadataExchange="true" 
                   enableGetWsdl="true" 
                   enableHelpPage="true" 
    metadataLocation=
    "http://MyHost.com/AdderService/Service.wsdl"/>
             </behavior>
            </serviceBehaviors>
          </behaviors>
          <serviceHostingEnvironment 
    aspNetCompatibilityEnabled ="true"/>
       </system.serviceModel>
    </configuration>
    
  17. Save the configuration.

  18. Compile the project.

  19. Execute the suite of tests.

Migrating ASP.NET Web Service Clients to the Windows Communication Foundation

  1. Ensure that a comprehensive suite of tests exist for the client.
  2. Use Visual Studio 2005 to upgrade the client application to .NET 2.0. Execute the suite of tests.
  3. Remove ASP.NET proxy code from the client project. That code will usually be in modules generated using the wsdl.exe tool.
  4. Generate Windows Communication Foundation proxy code using the svcutil.exe tool. Add that code to the client project, and merge the configuration output into the client's existing configuration file.
  5. Compile the application. Repair the compilation errors by replacing references to the former ASP.NET proxy types with references to the new Windows Communication Foundation proxy types.
  6. Execute the suite of tests.

Summary

Whereas the ASP.NET Web services tools are solely for building Web services, the Windows Communication Foundation provides tools for use in any circumstance where software entities must be made to communicate with one another. Even for Web service development projects, the Windows Communication Foundation supports more Web service protocols than ASP.NET Web services support. Those protocols provide for more sophisticated solutions involving, amongst other things, reliable sessions and transactions. The recommended course of action in most cases is to adopt the Windows Communication Foundation for new development, while continuing to maintain existing ASP.NET Web service applications. That course of action yields the benefits of the Windows Communication Foundation, while sparing the cost of migrating existing applications. New Windows Communication Foundation applications will be able to use existing ASP.NET Web services, and can co-exist with existing ASP.NET applications. The Windows Communication Foundation can even be used to program new operational capabilities into existing ASP.NET applications by virtue of the Windows Communication Foundation's ASP.NET compatibility mode.