Getting Started with Windows Communication Framework

One of the talks I'm giving at the upcoming DevConnections conference in Las Vegas the week of November 6th covers moving from Web Services to Service Oriented Architectures (SOAs).  Part of the talk will discuss how well Windows Communication Framework (WCF) satisfies SOA principles so I thought I'd put together a step by step tutorial on building a simple WCF service and client.  To run the examples you'll need the .NET 3.0 components from Microsoft as well as the VS.NET 2005 "Orcas" additions (optional).  At the time this blog was written .NET 3.0 wasn't available in a production release but that will change very soon.

WCF services can be exposed in several different ways such as HTTP and TCP.  For this example I'll demonstrate how to build a simple HTTP WCF service that is hosted using Internet Information Services (IIS).

Step 1:  Defining the Data Contract

A data contract defines the data that will be passed between a service and a client.  To create a data contract using WCF there are different attributes that can be used.  For this example I'll use the DataContract and DataMember attributes.  The DataContract attribute applies to a class whereas the DataMember attribute applies to a field or property (although I'd highly recommend using public properties as opposed to public fields).  An example of creating a simple contract to exchange data between a service and a client using a Customer class is shown next:

[DataContract]
public class Customer {
    
string _FirstName;
    string 
_LastName;

    
[DataMember]
    
public string FirstName  {
        
get return _FirstName}
        
set { _FirstName = value; }
    }

    [DataMember]
    
public string LastName  {
        
get return _LastName}
        
set { _LastName = value; }
    }
}

I typically prefer to model my data contracts using XML schemas (.xsd files).  This way I know that messages exchanged between the client and service are based upon global standards which helps to eliminate interop issues across different platforms.  With .NET you can use the xsd.exe tool (with the /classes switch) to generate classes.  In WCF you can use the new svcutil.exe tool.  For example, to automatically generate a data contract class from an existing schema the following can be run at the command prompt:

svcutil.exe /dconly schemaName.xsd

The /dconly switch says to create the data contract class from the types defined in the schema.

Step 2: Defining the Service Interface 

Once the data that will be passed between the service and client is defined (the data contract) you can create the service interface.  This is also done using WCF attributes.  In this example I'll use the ServiceContract and OperationContract attributes.  An example of using them in an interface named ICustomerService is shown next:

[ServiceContract()]
public interface ICustomerService
{
    [OperationContract]
    Model.Customer[] GetCustomers()
;
    
[OperationContract]
    Model.Customer GetCustomer(
string custID);
}

This interface defines two members named GetCustomers and GetCustomer.  With .NET 1.1, attributes defined on interfaces wouldn't carry over to the class that implements the interface.  Fortunately, in .NET 2.0 this has been changed and the attributes will follow the interface wherever it is implemented.

Step 3: Defining the Service

A WCF service is easy to create whether you have the VS.NET 2005 .NET 3.0 tools installed or not.  Services exposed using IIS has a .svc file extension rather than the .asmx extension used with ASP.NET Web Services.  The .svc file contains a ServiceHost attribute that points to a code-behind file that contains the actual service code. 

<% @ServiceHost Language=C# Service="CustomerService" 
CodeBehind
="~/App_Code/Service.cs" %>

Since the data contract and service interface have already been defined, creating a service is straightforward and only requires that the ICustomerService interface be implemented:

public class CustomerService : ICustomerService
{
    
public Model.Customer[] GetCustomers() 
    {
        
return Biz.BAL.GetCustomers();
    
}
    
public Model.Customer GetCustomer(string custID)
    {
        
return Biz.BAL.GetCustomer(custID);
    
}
}

Looking through the service code you'll notice that the methods delegate all work to a business layer class named BAL that is in the Biz namespace.  The BAL object in turn calls a data layer object named DAL which handles all database calls.  This layered architecture is a good practice to follow for many reasons one of which is code re-use across different types of applications.  For example, a local ASP.NET application could call the business layer code directly rather than having to serialize/deserialize messages by calling the WCF service (although in cases where you want all applications to call the service (following SOA principles) it's very efficient now if .NET 3.0 components are available to use by the applications). For those that need to add additional behaviors to services such as transaction support, the ServiceBehavior attribute can be applied to the service itself and related attributes can be applied to methods.

Step 4: Creating a Service Client

To create a service client that can consume the service created in steps 1 - 3 you can use the svcutil.exe tool and run the following at the command-prompt:

svcutil.exe http://www.site.com/service.svc?wsdl

The utility will generate a proxy class (C# or VB.NET can be choosen using switches) based upon the types and operations defined in the Web Service Description Language (WSDL) file that can be used to call the service.  It will also generate configuration data for the client application to use.  An example of the default configuration data generated by the tool that can be used to call the customer service is shown next:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    
<system.serviceModel>
        
<bindings>
            
<wsHttpBinding>
                
<binding name="WSHttpBinding_ICustomerService" 
                          closeTimeout
="00:01:00"
                          openTimeout
="00:01:00" receiveTimeout="00:10:00" 
                          sendTimeout
="00:01:00"
                          bypassProxyOnLocal
="false" transactionFlow="false" 
                          hostNameComparisonMode
="StrongWildcard"
                          maxBufferPoolSize
="524288" 
                          maxReceivedMessageSize
="65536"
                          messageEncoding
="Text" textEncoding="utf-8" 
                          useDefaultWebProxy
="true"
                          allowCookies
="false">
                    
<readerQuotas maxDepth="32" maxStringContentLength="8192" 
                                maxArrayLength
="16384"
                                maxBytesPerRead
="4096" 
                                maxNameTableCharCount
="16384" />
                    <
reliableSession ordered="true" 
                                inactivityTimeout
="00:10:00"
                                enabled
="false" />
                    <
security mode="Message">
                        
<transport clientCredentialType="Windows" 
                                      proxyCredentialType
="None" realm="" />
                        <
message clientCredentialType="Windows" 
                                      negotiateServiceCredential
="true"
                                      algorithmSuite
="Default" 
                                      establishSecurityContext
="true" />
                    </
security>
                
</binding>
            
</wsHttpBinding>
        
</bindings>
        
<client>
            
<endpoint address="http://localhost:1810/SOAService/Service.svc"
                    binding
="wsHttpBinding" 
                    bindingConfiguration
="WSHttpBinding_ICustomerService"
                    contract
="ICustomerService" 
                    name
="WSHttpBinding_ICustomerService">
                
<identity>
                    
<userPrincipalName value="DAN-LAPTOP\wahlind" />
                </
identity>
            
</endpoint>
        
</client>
    
</system.serviceModel>
</configuration>

An example of calling the service using the WCF client proxy is shown next:

protected void Page_Load(object sender, EventArgs e)
{
    CustomerServiceClient proxy 
= new CustomerServiceClient();
    
lblOutput.Text proxy.GetCustomer("ALFKI").ContactName;
}

Conclusion 

That's all of the steps required to create a simple WCF service. While it could be argued that ASMX services are easier to create (slap a WebMethod attribute on a method and you're basically done), I really like the fact that creating WCF services encourages developers to follow contract-first design principles (designing the data contract, service interface and then the service).  This should help with Web Service interoperability issues between different platforms and languages.  There is of course much more to WCF than has been covered in this article.  Read more about it at http://msdn.microsoft.com/webservices.

The code for this example can be downloaded at the following URL:

http://www.xmlforasp.net/codebank/Download/Blog/WCFDemo.zip

 

Published Sunday, October 29, 2006 9:02 PM by dwahlin
Filed under: ,

Comments

# re: Getting Started with Windows Communication Framework

Friday, January 19, 2007 11:14 AM by Kenny

Hi, you wrote, that you use xsd files for the data contracts. Me too ;o). I used the svcutil- tool, but it generates an error - message:

One of my complex types can`t get imported. Is there another way to solve this problem (datacontract.xsd to *.cs - class), and how can i declare my [data member] in this XSD-file???

K.

# re: Getting Started with Windows Communication Framework

Monday, January 22, 2007 5:52 PM by dwahlin

I'm guessing that a namespace is causing the issue potentially although to generate my "model" classes I'm still using xsd.exe for the most part.

# re: Getting Started with Windows Communication Framework

Friday, March 9, 2007 3:12 PM by Andy Koval

Hi,

I'm new to WCF. If I've got an observerableCollection that I'm using in WPF, would I adorn the collection with [DataContract] and [DataMember] attributes. If so how...?