WSCF Walkthrough

Please download the sample for a complete implementation. 

Introduction

In a Web services-based world communication is based on public contracts the technical and partly semantic description of a Web service. The most common kind of contract, the technical interface definition, is today expressed in the form of WSDL. Developers can select either a code-first approach or contract-first approach for creating their contracts. Visual Studio 2005 natively supports coding your types and classes and let the ASMX runtime generate the WSDL. There is also a way you can go a code-based contract-first route by using all the necessary attributes from the System.Web.Services and System.Xml.Serialization namespaces. However, schema-based contract-first design is in many cases the better approach because you define the service messages in XML type system of the SOAP standard, not the type system of any given programming language. This approach makes your services more interoperable and emphasizes the fact that a message is fundamentally different from a local class. In that sense it is seems more than natural to define your contract-first and generate the code skeleton for the different platforms and programming languages from it. Generally there are five distinguished steps in contract-first design.


Figure 1: Five steps for schema-based contract-first modeling

Five steps of a schema-based contract-first design approach:

Modeling Data
This step involves defining data structures which should be exchanged in messages through your Web service interface.

Modeling Messages
In this step you define the messages you need to exchange by using XML Schema ideally with an editor like Altova XmlSpy or the integrated VS 2005 XSD editor.

Modeling Interface and Operations
Define which operations you want to offer to the Web service consumer. Operations are made up of message exchange patterns (e.g. one-way or request/response) and their associated messages.

Generate Code Skeletons
Based on the data, message and interface contracts defined in the previous steps; generate your platform and programming language code as appropriate.

Iterate Contract Design and Code Generation
The biggest challenge for schema-based contract-first approach today is lack of modeling concepts and tools support. WSCFs goal is to fill that gap and simplify schema-based contract-first design for developers and architects. The tool provides a handful of features to help you focus on the important steps, the message and operation design, instead of generating boiler-plate code. WSCF is tightly integrated into Visual Studio 2005 as an add-in, so developers and architects continue to work within their favorite IDE. This document demonstrates how to use WSCF and its features for your Web services.

Demo Application

Scenario

For this walkthrough we are going to create a Web service for browsing restaurants in your area. It also allows administrators to add new restaurants to the service.

Organizing the Visual Studio 2005 Solution

First lets create a blank Visual Studio 2005 solution. We will add our projects into this solution afterwards. The following table shows the projects to add to the solution, the project type and their purposes.

Project Type Description
ContractMetadata C# Empty Project Contains the metadata definitions for data, messages, and interfaces
RestaurantService C# Empty Web Site (File system based) Restaurant Web service implementation
RestaurantClient C# Console Application Restaurant Web service client implementation
Table 1: Projects in the sample solution

It is important to note that we do not add a typical ASP.NET Web Service project to our solution, but rather a C# Empty Web Site. This is obvious as we want to omit the automatic and template driven code generation of our Web service code in the first place. Instead, we want to take the contract-first route.

If all three projects are added to the solution, your Solution Explorer window might look like the following figure/ screenshot:


Figure 2: Sample VS 2005 solution with empty projects

Modeling Data and Messages

Modeling Data

Now that we have organized our Visual Studio 2005 environment, we can start defining our contract. According to the steps we discussed above, our first step is to create an XML Schema which represents the data we want to use. For our RestaurantService Web service, we need two data structures to represent the Restaurant information and a collection of Restaurant information. The following figure depicts our XML schema data defined in a newly added RestaurantData.xsd file to the ContractMetadata project.


Figure 3: Data structures for Restaurant sample

As stated in the previous section, our service allows administrators to add new restaurants to the service. However, we want to allow access only to administrators we trust. Therefore it would be necessary for RestaurantService administrators to submit their credentials for authentication before adding a new restaurant. We want to use SOAP headers to transport the authentication credentials (hopefully over an SSL connection J). Thus we need another data structure to carry our user credentials. Since we want to separate the data structures used in the SOAP Headers from the ones used in the SOAP Body, lets define the user credentials in a new XSD file called RestaurantHeaderData.xsd.


Figure 4: Header data for Restaurant sample

Modeling Messages

Next, we are defining the messages to exchange our data. Lets start by adding a new XSD file called RestaurantMessages.xsd. So far, we have three messages for our RestaurantService Web service. In our case Visual Studio defined the messages as XML elements with embedded (anonymous) complex types. Alternatively we can first model our complex message types and then instantiate them by adding elements of the appropriate types to the message schema RestaurantMessages.xsd. It is very important to note that we have to <import> the XSD file which defines our data structure (RestaurantData.xsd in this case) into RestaurantMessages.xsd. This way we are reusing the XML types we modeled before. In real world projects your data type schemas might already exist in some other context, and you can just add it to your project and reference in the message schema. In our example we simply use the xsd:import mechanism.


Figure 5: Messages for the Restaurant sample


Figure 6: Illustration of the schema import mechanism for the data structures

Modeling the Interface Contract

As explained at the beginning of this article, interface contracts are expressed as WSDL documents. Obviously we do not want to hand-craft WSDL; nobody wants to because its very tedious and at some point staring at all the angle brackets blurs your vision. Even though there are several WSDL editors available, those editors just try to focus too much on the actual WSDL specification. What we need is a higher-level, a more abstracted view on the interface contract.

WSCF comes with its own WSDL generation wizard that tries to hide any suspicious details of the WSDL specification from the developer or architect. This wizard allows you to do all your interface contract creation work just inside the Visual Studio 2005 IDE. It hides all the complex details of the specification and generates a WSDL document, which is totally compliant with the interoperability recommendations of WSI BP (Basic Profile) 1.1.

WSCF also allows the user to import multiple schema files to create a WSDL document. Not only that, it also provides a feature to automatically infer operations from your message schema definition. This saves a lot of time you might otherwise spend manually adding operations, even though they could be guessed from the message names.

The following table shows the currently supported string patterns for operation inference.

Input Message Name Element Output Message Name Element Inferred Operation Name
XYZ XYZResponse XYZ
XYZRequest XYZResponse XYZ
XYZRequestMessage XYZResponseMessage XYZ
Table 2: Supported string patterns for operation inference

The latest version of WSCF (version 0.6) also armed with a feature which you can preferably use to generate the SOAP 1.2 binding. Last but not least, the wizard enables a simple kind of WSDL round-tripping, which means you can generate a WSDL, review it, and later modify it using the wizard. Lets take a look at how the WSDL wizard generates the interface contract for the RestaurantService.

To start the WSDL wizard right click on your messages schema file (RestaurantMessages.xsd in this case) in the Visual Studio Solution Explorer and select the Create WSDL Interface Description menu item from the context menu.


Figure 7: Starting the Web service interface design Step

The wizard starts with a welcome message. Click on the Next button to collect some basic metadata for our Web service description. This includes the service name, the Web services XML namespace and an optional description.


Figure 8: Collecting basic metadata for the Web service interface description

The next step allows you to import additional schema files into the WSDL. In our example we have to import the schema file with the type definitions for our message headers, RestaurantHeaderData.xsd. Its important to keep in mind that you cannot import two schema files which have the same target namespace.


Figure 9: Specifying additional XML schema imports

In step 3 you define the operations in your contract the meat of this process. However, WSCFs WSDL wizard is intelligent enough to infer the operations according to message name patterns in your message schemas. Click on the Infer Operations checkbox to automatically infer the operations.


Figure 10: Using operation inference

The following step allows you to optionally configure inbound and outbound messages in your operations. In this step you can select the message element for your message body from the schema. However, if you inferred the service operations, message body elements are automatically and hopefully correctly - configured for you. Optionally you can customize the name for your inbound/outbound message.


Figure 11: Specifying messages and optional properties

The most interesting feature on Step 4 is the Message headers checkbox. When you check that box, you can select schema types for one or more message headers for your inbound/outbound messages from the drop-down underneath the checkbox. In this case we create a message header for addRestaurantIn inbound message to carry our user credentials. Check the Message headers checkbox and select Credentials from the dropdown.


Figure 12: Specifying message headers

In the next step you can select whether you want to have the <service> tag in your WSDL. <service> allows you to specify a default location of a Web service that implements the abstract interface you are currently modeling. Also, you can select whether you want to start the code generation immediately after the wizard finishes. The WSDL specification defines <service> as optional, but some Web services frameworks dont work correctly unless the <service> element is present in the WSDL document. In addition to the above features WSCF 0.6+ versions give you an option to generate the SOAP 1.2 binding in the generated WSDL.


Figure 13: Specifying additional options

Step number 6 allows you to modify the relative paths of your imported schema files. This may be needed if you have the real XSD files (e.g. for production use) at a different location, e.g. at a URL on a web server.


Figure 14: Changing the paths to the XSDs

Click the Finish button on the last screen and you will see the generated WSDL file in your solution explorer. Voila.


Figure 15: Generated WSDL inside the metadata project

Code Generation

For this walkthrough we want to focus on the .NET platform and Visual Studio 2005 as our IDE. Thus, we now need to distribute or deploy - the contract metadata to both the client and the Web service developer as shown in the following picture.


Figure 16: Deploying the Web service's metadata to the developers

Service-side Code Generation

Now that we have our service contract in place we can start building our service implementation. WSCF comes with a code generation engine for C# and Visual Basic.NET. Lets start by generating the service-side code first. To start the code generation, we have to right click on the WSDL file in our RestaurantService project and select the Generate Web Service Code menu item from the context menu.


Figure 17: Generating code for the service side

This action will bring up the code generation options dialog box which allows you to control the code generation.


Figure 18: Code generation options for the Web service

Since we generate the code for the service, we select the Service-side stub radio button. Various checkboxes in the Options group box let you generate the code according to what you actually need. We will take a look at each of those options in a moment. Now it is important to notice that some options are available for both client-side and service-side code generation, whereas some others are only available for client-side or service-side.

Public properties

Clicking the Public properties checkbox commands the code generator to generate property getters and setters for our data fields. For example instead of generating a public field called Name in the RestaurantInfo class (Listing 1), WSCFs code generation will create a property getter and setter (Listing 2).

public class RestaurantInfo
{
    public string Name;
}

Listing 1: Public fields in data transfer object

 

public class RestaurantInfo
{
  private string name;

  public string Name
  {

    get
    {
      return this.name;
    }
    set
    {
      if ((this.name != value))
      {
        this.name = value;
      }
    }
  }
}

Listing 2: Private fields and get/set properties in data transfer objects

Serializable

Selecting the Serializable classes option will cause the code generator to annotate our types with the [Serializable] attribute (Listing 3). This makes them runtime serializable (by using e.g. System.Runtime.Serialization.Formatters.Binary.BinaryFormatter) as opposed to being just XML serializable by the XmlSerializer.

[Serializable()]
public class RestaurantInfo
{
  //
} 

Listing 3: Serializable data transfer objects

Collections

The Collections option instructs the code generator to generate a .NET collection type instead of arrays where necessary. For example, the GetRestaurantResponse class could contain a list of RestaurantInfo objects as an array. By default WSCF would generate an array of RestaurantInfo objects (Listing 4).

// Collection type generated.
public class RestaurantInfoCollection : System.Collections.CollectionBase
{
}

public class GetRestaurantsResponse
{
  // Array declaration is modified to use the newly created
  // collection type.
  public RestaurantInfoCollection restaurants;
}

Listing 5: List-like data in generated collection types

List<T>

In addition to Collections generation, now you have the liberty to use the generic List (List<T>) type to represent the collection. This makes the collection manipulation strongly typed with a few lines of code. To turn on this feature, select the List<T> check box in the code generation dialog box. Listing 6 demonstrates the code generated instead of an array of RestaurantInfo type.

public System.Collections.Generic.List<RestaurantInfo> restaurants;

Listing 6: List<T> type collection generation

Data binding

WSCF also supports generating the data binding code if necessary. This feature could be turned on by selecting the Data binding check box in the code generation dialog box. The resulting types will automatically include the implementation of INotifyPropertyChanged interface (Listing 7). This feature could be primarily used by the client applications. However you could also use it in the service side implementation as well.

public partial class RestaurantInfo : 
 object, System.ComponentModel.INotifyPropertyChanged { public int RestaurantID { get { return this.restaurantID; } set { if ((this.restaurantID != value)) { this.restaurantID = value; this.RaisePropertyChanged("RestaurantID"); } } } }

Listing 7: Data binding option will generate the INotifyPropertyChanged implementation

Order Identifiers

The Order Identifiers option will cause the code generator to emit the order identifiers for the public fields and properties. These order identifiers will control the order of the elements generated by the XmlSerializer (Listing 8).

[System.Xml.Serialization.XmlElementAttribute(Order=0, ElementName="restaurantID")]
public int RestaurantID; [System.Xml.Serialization.XmlElementAttribute(Order=1, ElementName="name")] public string Name;

Listing 8: Order identifier generation

Adjust Casing

Typically the programming languages (.NET) and XML follow different naming convention patterns. More specifically, the XML world mostly tend to use the Camel case naming convention where as the programming languages tend to use the Pascal case naming convention. WSCF has the Adjust Casing option to easily switch between these two worlds. If you select this option the resulting code will convert all the type names and public field names to Pascal case if necessary. For instance, if we take a look at the RestaurantInfo complex type in RestaurantData.xsd and the respective .NET type generated to represent it you can easily notice the difference (Listing 9).

<xs:complexType name="restaurantInfo">
  <xs:sequence>
    <xs:element name="restaurantID" type="xs:int" />
    <xs:element name="name" type="xs:string" />
  </xs:sequence>
</xs:complexType>

public partial class RestaurantInfo
{
  public int RestaurantID;
  public string Name;
}

Listing 9: Casing adjustments

No XxxSpecified handling

Another interesting feature in WSCF is; automatic Specified handling. Typically, when there are optional elements in an inbound/outbound message, the XmlSerializer counts on a Boolean field to detect whether the field value is actually specified or not. For example, if our openFrom element in restaurantInfo complex type is optional, XmlSerializer will check openFromSpacified field before serializing its value. Therefore in order to successfully serialize these optional values, we should set a proper value and set the corresponding Specified field to true. WSCF takes off the burden of this extra step by turning on this Boolean field automatically from the property setters (Listing 10). However, if this could break your existing implementation logic, you can turn off this feature by selecting the No xxxSpecified Handling check box.

public System.DateTime OpenFrom
{
  get
  {
    return this.openFrom;
  }

  set
  {
    if ((this.openFrom != value))
    {
      this.openFrom = value;
      this.RaisePropertyChanged("OpenFrom");
      this.openFromSpecified = true;
    }
  }
}

Listing 10: Automatic specified handling

Separate Files

The Separate Files option, generates types into individual .cs/.vb files. In many situations this will make code organization easier as it generates a separate file for each type. Furthermore it generates AsmxHelpModule.cs, RestaurantService.asmx, AsmxHelpPage.aspx and Web.config files. AsmxHelpModule.cs contains an implementation of an Http module which avoids users from viewing the WSDL by passing wsdl parameter to the Service endpoint URL (This only happens if the Enable WSDL option is turned off). This Http module is hooked up to the ASP.NET plumbing via the configuration settings in the Web.config file. As its name implies RestaurantService.asmx has the ASMX endpoint code. Web.config file contains the configuration settings of the Web Service. For example it will have the necessary entries to avoid accessing AsmxHelpPage.aspx, which contains the implementation for Web Service Help and Documentation page.

WSCF 0.6 version also introduces a feature to expose your modeled WSDL via the .asmx endpoint. This will allow the users to view the modeled WSDL by passing ?wsdl parameter to the .asmx endpoint. You can turn on this feature by selecting the Enable WSDL check box in the code generation dialog box. (Please see the Enabling WSDL section below for a discussion about a minor design issue in this feature)

There is one more option we did not discuss yet (Old async code). We will be looking at it in the next section because it is relevant for the client side code generation. So, according to the options selected for service-side code generation our solution explorer will look something like the following picture.


Figure 19: Generated code for the service side

Other options

Optionally we could have also selected the Validate messages option in the code generation dialog box. This would wire up a custom SoapExtension to the ASP.NET request processing plumbing to validate the messages received by the Web service against our contract schema.

Last but not least, we can specify the desired names for the ASMX file and the .NET code namespace for the generated code by using the Service filename and Service namespace textboxes, respectively.

Client-side Code Generation

We start generating the code for the client side by selecting the code generation option for the RestaurantService.wsdl file in the RestaurantClient project.


Figure 20: Starting up code generation for the client side

This action will bring up the same code generation dialog box we saw earlier when generating the code for the service side. However, this time we are going to select the Client-side proxy radio button in the General group box.


Figure 21: Code generation options for the client

The behavior of the common options is the same as described in the service-side code generation section. We also have two additional options for proxy code generation. Configure endpoint option will generate an entry in the app.config or web.config file to configure the services communication endpoint URL.

Optionally we can select the Access messages option and access the SOAP messages we exchange with the service through read-only properties on the proxy class.

Old async code

By default, the proxy code generation uses the new event-based asynchronous pattern in the generated code (For more information please see this link). However, WSCF also has an option to specify whether you want to follow the old asynchronous programming pattern or not (Delegate-based asynchronous pattern).  

We can specify the proxy filename and the namespace name for the generated code by using Proxy filename and Proxy namespace textboxes respectively.

Finally hitting the Generate button will output the code and our Solution Explorer will look something like the following picture.


Figure 22: Generated code for the Web service client

It is also interesting to notice that WSCF will generate a new task in the Task List to remind you to configure your actual Web service endpoint.


Figure 23: Task indicating to check the Web service's endpoint in config

Now that we have the skeleton for our code we can continue with our actual service and client implementations. Essentially the service developer will open the code behind file of the .asmx file (RestaurantService.asmx.cs in this case) and implement each of the methods which throw a NotImplementedException. The developer of the client side will instantiate an instance of the proxy class and call its methods in order to communicate with the service. Please download the Visual Studio 2005 solution for the complete implementation.

Iterating the Contract and Code

It is quite obvious that nobody in the world will create a 100% accurate contract at the first time. WSCF comes with WSDL round-tripping feature which allows you to modify your existing contracts with the minimum effort. Please note that we are not talking about modifying a published and released contract but rather working in an iterative fashion on one instance of a WSDL-based interface description during development.

Lets take a look at how we can use this feature in our application. Lets start adding a new simple data type to resemble the newly introduced rating information into the RestaurantData.xsd file in the ContractMetadata project.


Figure 24: Adding a rating data structure to the metadata

Now we have to change our message schema as well and define a new message to exchange our RatingInfo data.


Figure 25: Updated messages schema for rating restaurants

Our next step is to add a new operation to our interface contract. Again, we do not want to hand-craft the WSDL. This time we use the WSDL round-tripping feature to alter our existing RestaurantService.wsdl file. We can start round-tripping by right clicking and selecting the Edit WSDL Interface Description menu item from the context menu.


Figure 26: Starting up WSDL round-tripping

This brings up the same WSDL wizard we used to create our original service description with. However, this time it is pre-populated with the values from the RestaurantService.wsdl file. When we select the Infer Operations checkbox in step 3 we can see a newly added operation called rateRestaurant in our operations list.


Figure 27: Interface with update operations list

In step 5 the WSDL wizard forces you to generate the code because if doing any changes in the WSDL means you really should regenerate the code. However, in this case we just omit the code generation as we modify the contract inside the ContractMetadata project.

Now that we have our modified contract we must copy it to the RestaurantService and RestaurantClient projects, respectively. We generate the code for each project like we did in the previous sections. However this time we can select the Overwrite existing files option, while generating code for both client side and service side. This will replace the existing files by the newly generated files. Otherwise the code generation will automatically produce unique file names to preserve your existing code.


Figure 28: Overwriting existing code files while generating code

Diving into the Generated Code

In our demo application walkthrough we saw how straight-forward it is to build our web services with WSCF in a contract-first way. However, in this section we are going to show you some more interesting things that WSCF code generation does for you.

CLR Interface Generation

If you take a look at the generated code for our restaurant service application, you will see that the code generation generates a CLR interface for our web service interface. Developers can use this interface and implement it in various layers in their application. For instance, one can take this interface and implement it in an Enterprise Services component which handles the business logic for the restaurant service.

public interface IRestaurantServicePort
{
  GetRestaurantsResponse GetRestaurants(
  GetRestaurants getRestaurants1);
  void AddRestaurant(AddRestaurant addRestaurant1);
}

Listing 11: Generated CLR interface

Not Implemented Exceptions

Generated code for the service side automatically adds a statement to throw a NotImplementedException from all WebMethods. This ensures that the code compiles successfully and also complies with the .NET Framework design guidelines.

public virtual GetRestaurantsResponse GetRestaurants(
[System.Xml.Serialization.XmlElementAttribute("getRestaurants",
Namespace= "urn:thinktecture-com:demos:restaurantservice:messages:v1")]
GetRestaurants getRestaurants1) { throw new System.NotImplementedException(); }

Listing 12: NotImplementedExceptions in WebMethods

ASMX Documentation Page

In WSCF 0.5+ versions, the service-side code generation now creates a modified ASMX documentation page. It avoids viewing the WSDL by using wsdl parameter in the URL by returning an HTTP 404 error.

Enabling WSDL

As mentioned above, WSCF allows you to expose the modeled WSDL via the ASMX endpoint. However, we came across a minor problem which we had to leave it as it is in our RTM.

When Enable WSDL option is turned on, our modeled WSDL is imported into an ASMX generated WSDL (See the highlighted code in blue color in listing 13). This ASMX generated WSDL defines the two <wsdl:port> elements to expose the SOAP 1.1 and SOAP 1.2 bindings.

<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions>
  <wsdl:import namespace="urn:thinktecture-com:demos:restaurantservice:webservice:v1"
    location="Contract/RestaurantService.wsdl" />
  <wsdl:types />

  <wsdl:service name="RestaurantServicePort">
    <wsdl:port name="RestaurantService" binding="tns:RestaurantService">
      <soap:address
        location="http://localhost:1633/RestaurantService/RestaurantService.asmx" />
    </wsdl:port>
    <wsdl:port name="RestaurantService1" binding="tns:RestaurantService">
      <soap12:addres
        location="http://localhost:1633/RestaurantService/RestaurantService.asmx" />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions> 

Listing 13: ASMX generated WSDL

However, due to the design of ASMX runtime the <wsdl:port> generated for SOAP 1.2 is pointing to the same binding as the SOAP 1.1 (take a look at the code highlighted in red color above). Therefore when we generate the sample SOAP 1.2 message in the operation help page; we look for the <wsdl:port> element for the actual SOAP 1.2 binding and if it is not available we leave the SOAP address and the host fields blank.

For example for the above service, the correct sample SOAP 1.2 message should look like:

POST /RestaurantService/RestaurantService.asmx HTTP/1.1
Host: localhost
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope>
</soap12:Envelope> 

Listing 14

Since we do not have the wsdl:port element, the generated sample message would actually look like:

POST [SOAP address not available] HTTP/1.1
Host: [Host not available]
Content-Type: application/soap+xml; charset=utf-8
Content-Length: length

<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope>
</soap12:Envelope> 

Listing 15

Command Line Tool

WSCF is also offering a command line interface to the code generation engine (wscf.exe). All code generation can thus be included in your batch scripts and/or build process. The following screenshot shows all available options of wscf.exe.


Figure 29: wscf.exe command line tool parameters (click on the image to enlarge)

Conclusion

Well, not everybody will buy the schema-based contract-first approach. This is fine. This is what we expect. But we surely know out of our own experience and from customers feedback so far: Yes! This is what we are doing since years and WSCF just makes it a bit easier.

For feedback, please send email to the WSCF team at thinktecture.