Web and SOAP via WCF

WCF in Orcas introduces several new types that, among other things, dramatically simplify the work needed to write applications that adhere to the protocols of the web. These types of services are commonly described as having a REST-ful architectural style. For the most part, a REST-ful architectural style implies three features:

1) embracing a URI as the way to identify an operation (as opposed to SOAP Action header blocks)

2) the universal nature of HTTP GET in HTTP Invokes (PUT, POST, DELETE, other)

3) Content type is the data model (XML, JSON, binary, etc.)

WCF is Orcas is almost exclusively built upon extensibility points introduced in WCF v1 (or .NET v3). With that in mind, you can view the Web/REST capabilities of WCF in Orcas as something that could have been built with WCF V1. While this was certainly possible, you would have had to write quite a bit of infrastructure code. Since infrastructure code is typically the hardest to write and test, the introduction of the Web/REST capabilities of WCF in Orcas means that you can focus on the application logic rather than infrastructure code. The impact is that you can deliver more robust services in less time.

One of the ways WCF in Orcas surfaces the new Web/REST capabilities to developers is a new set of attribute annotations in a service contract:

    1:  [ServiceContract]
    2:  public interface IWebAndSoapContract { 
    3:   
    4:    [WebGet(UriTemplate = "SomeOperation")]
    5:    [OperationContract]
    6:    String SomeOperation(); 
    7:   
    8:    [WebInvoke(Method="TEST",
    9:               UriTemplate="SomeOtherOperation")]
   10:    [OperationContract]
   11:    SomeDataContract SomeOtherOperation(SomeDataContract dataContract); 
   12:   
   13:  } 

The WebGetAttribute annotation indicates that the operation will be available via an HTTP GET. The WebInvokeAttribute annotation indicates that the operation should be available via an HTTP operation indicated by the value of the Method property (in this case "Test"). There's much more to say about these attributes. I'll save that for another blog post. The important point to notice is that the contract is nearly identical to a contract in WCF V1.

NOTE: the SomeDataContract type is just a simple data contract.

 The WebHttpBinding and WebServiceHost are other types that simplify creating Web/REST services. Like other bindings, the WebHttpBinding creates channel listeners and channel factories. These channel factories and channel listeners, however, are specifically for Web/REST services. I'll discuss what this binding acutally does in subsequent posts. The WebServiceHost, on the other hand, adds some ServiceDescription validation specific to Web/REST services. I'll talk more about these validations in subsequent posts.

Adding the WebGetAttribute and the WebInvokeAttribute annotation to a service contract does not limit that contract to Web/REST services. You can still use the contract with the ServiceHost that shipped with WCF V1. After all, the attribute annotation is nothing more than extra metadata.

The following code snippet illustrates how to use the new Web/REST types and how a single contract can be used for SOAP and Web/REST endpoints:

    1:   sealed class Receiver : IWebAndSoapContract { 
    2:   
    3:    public String SomeOperation() {
    4:      Console.WriteLine("SomeOperation invoked");
    5:      return "SomeOperation invoked"
    6:    } 
    7:   
    8:    public SomeDataContract SomeOtherOperation(SomeDataContract dataContract) {
    9:      Console.WriteLine("SomeOtherOperation invoked");
   10:      return dataContract;
   11:    } 
   12:   
   13:    static void Main() {
   14:      // web host
   15:      WebHttpBinding webBinding = new WebHttpBinding();
   16:      WebServiceHost webHost = new WebServiceHost(typeof(Receiver), 
   17:        new Uri("https://localhost:8000"));
   18:      webHost.AddServiceEndpoint(typeof(IWebAndSoapContract), 
   19:        webBinding, "webBinding");
   20:      webHost.Open();
   21:      Console.WriteLine("The web host is open"); 
   22:   
   23:      // v1 service host
   24:      NetTcpBinding tcpBinding = new NetTcpBinding();
   25:      ServiceHost v1Host = new ServiceHost(typeof(Receiver), 
   26:        new Uri("net.tcp://localhost:7000"));
   27:      v1Host.AddServiceEndpoint(typeof(IWebAndSoapContract), 
   28:        tcpBinding, "tcpBinding");
   29:      v1Host.Open();
   30:      Console.WriteLine("the v1 host is open"); 
   31:   
   32:      // web client
   33:      ChannelFactory<IWebAndSoapContract> factory = 
   34:        new ChannelFactory<IWebAndSoapContract>(webBinding, 
   35:          new EndpointAddress("https://localhost:8000/webBinding"));
   36:      factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
   37:      IWebAndSoapContract proxy = factory.CreateChannel();
   38:      SomeDataContract returnValue = proxy.SomeOtherOperation(
   39:        new SomeDataContract("testing"));
   40:      Console.WriteLine("web client sent and received data"); 
   41:   
   42:      // tcp Client
   43:      factory = new ChannelFactory<IWebAndSoapContract>(tcpBinding, 
   44:        new EndpointAddress("net.tcp://localhost:7000/tcpBinding"));
   45:      proxy = factory.CreateChannel();
   46:      proxy.SomeOperation();
   47:      Console.WriteLine("the tcp client sent and received data"); 
   48:   
   49:    } 
   50:   
   51:  } 

The web client part of the previous code snippet makes an HTTP "TEST" call to the HTTP service, and sends a serialized SomeDataContract object. The tcp client sends a SOAP message to the TCP service. In other words, a single contract can be used for both Web/REST services and SOAP services. It's cool stuff indeed.

In addition to just being cool, this type of functionality means that a choice today does not necessarily limit tomorrow's possibilities. In other words, you can use SOAP services today, and add services that use Web protocols tomorrow without a major overhaul.