If you're a fan of contract-driven development, you can take control of the WSDL chicken-egg situation.The .NET Framework makes it extremely easy to create and deploy Web Services; on top of that, Visual Studio makes it even easier still. You can add an .asmx file to your project, add a couple of methods, then build your solution, and you have just written a Web Service. This process is deceptively simple though, since a lot of things are going on in the background, including the generation of a WSDL on the server side and a client proxy on the client side. Visual Studio quietly handles all of this for you, which is not always a good thing, since Visual Studio assumes an XML-based RPC view of Web Services.There is an idea in Web Services development called contract-first or contract-driven development. The basic idea is that, instead of letting Visual Studio generate a WSDL file for you, you should write this WSDL file by hand using a WSDL, XML, or text editor.
WSDL stands for Web Services Description Language. It is an XML format used to describe a Web Service's interface by defining its operations and message exchange patterns. The actual messages are defined using a schema definition language like XSD.
Why would you want to write something by hand that Visual Studio will usually generate for you? Letting Visual Studio generate your WSDL is kind of like picking up a car on the lot and then letting the dealer take care of the contract for you. Sure, you get the carbut who knows what you are committed to. By writing your WSDL first, you have complete control over how your Web Service will be seen to the outside world, including its interface and data structures. This is really just a continuation of the idea of contract-driven development that has been around for yearsthe idea of first creating a contract that your object will implement and that your clients will consume.Writing the WSDL to your Web Services can be a task in and of itself. There are not really any great tools out there to help you along, and the format takes a little bit of getting used to. And after you have created your WSDL file, you then also need to create the client-side proxy and the server-side Web Service code. This is where the WSContractFirst add-in comes into play.The WSContractFirst add-in was written by Christian Weyer in an effort to make contract-first development easier. Using this add-in, you can generate the code for client-side proxies and server-side Web Services based on a WSDL file right inside of Visual Studio.First, you will need to download and install the add-in from http://www.thinktecture.com/resources/software/wscontractfirst/defaultl.
13.9.1. Generate Server-Side Interface
To demonstrate this tool, I am going to start with a very simple WSDL file. This WSDL file defines a simple service called UserValidation that includes a single method, ValidateUser. This method accepts an integer as its single parameter and returns a Boolean value, specifying whether this is a valid user or not. Here is the WSDL file I have created for this simple service:
After creating and saving this WSDL file, you then need to add it to your project. Once it is in your project, you then simply need to right-click on the WSDL file and you will see the option shown in Figure 13-21.
Figure 13-21. ContractFirst menu
You could alternatively open the tool from the Tools menu and simply point the tool to your WSDL file (including a URL); it does not have to be a part of your project.
After choosing Generate Web Service Code, you will see the dialog shown in Figure 13-22.
Figure 13-22. Code Generation Options
You can configure a number of things from this dialog. In this example, I have chosen to generate a Service-Side Interface. I have also renamed the file to better match the name of my service. After you click Generate, the add-in will create two different files for you. In this example, the first is named UserValidation.cs and is an abstract class defining the interface for the Web Service. The code for this file can be seen here:
//------------------------------------------------------------ // <autogenerated code> // This code was generated by a tool. // Changes to this file may cause incorrect // behavior and will be lost if // the code is regenerated. // </autogenerated code> //------------------------------------------------------------ // // This source code was auto-generated // by WsContractFirst, Version=0.3.1.4244 namespace TestServices { using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; /// <remarks></remarks> [System.Web.Services.WebServiceBindingAttribute( Name="UserValidationSoap", Namespace="http://namespaces.infozerk.com/")] public abstract class UserValidation : System.Web.Services.WebService { /// <remarks/> [System.Web.Services.WebMethodAttribute( )] [System.Web.Services.Protocols.SoapDocumentMethodAttribute( "http:/namespaces.infozerk.com/ValidateUser", RequestNamespace="http://namespaces.infozerk.com/", ResponseNamespace="http://namespaces.infozerk.com/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle= System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public abstract bool ValidateUser(int userID); } }
ContractFirst then also generates an .asmx file (including the corresponding .cs or .vb file) that inherits from this abstract class. This file can be seen here:
//------------------------------------------------------------ // <autogenerated code> // This code was generated by a tool. // Changes to this file may cause incorrect // behavior and will be lost if // the code is regenerated. // </autogenerated code> //------------------------------------------------------------ // // This source code was auto-generated // by WsContractFirst, Version=0.3.1.4244 namespace TestServices { using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; /// <remarks/> [System.Web.Services.WebServiceBindingAttribute( Name="UserValidationSoap", Namespace="http://namespaces.infozerk.com/")] [System.Web.Services.WebServiceAttribute( Namespace="http://namespaces.infozerk.com/")] public class UserValidationImpl : UserValidation { /// <remarks/> [System.Web.Services.WebMethodAttribute( )] [System.Web.Services.Protocols.SoapDocumentMethodAttribute(" http:/namespaces.infozerk.com/ValidateUser", RequestNamespace="http://namespaces.infozerk.com/", ResponseNamespace="http://namespaces.infozerk.com/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle= System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public override bool ValidateUser(int userID) { return new bool( ); } } }
Notice that the tool simply adds a stub return into the methodin this example, it will return a new bool value. From here, your job would be to implement this Web Service by writing the contents of this method, doing the actual work of determining whether this is a valid user.
13.9.2. Generate Client-Side Proxies
The other way that Visual Studio makes working with Web Services easy is the ability to add a web reference to a Web Service. After adding a web reference, you can then access that Web Service just as if it were a local class in your project, due to Visual Studio generating a client-side proxy for you. This proxy is simply a class that sits between your code and the actual Web Service. The ContractFirst add-in can generate client-side proxies as well, including some advanced options for code generation.For this example, I will use the same WSDL file used for the server-side example. After right-clicking on the WSDL file and choosing Generate Web Service Code, you will now need to choose Client-Side Proxy and specify a name of the proxy, as shown in Figure 13-23.
Figure 13-23. Client proxy creation
After you click Generate, the following code will be added to your project as UserValidationProxy.cs:
//------------------------------------------------------------ // <autogenerated code> // This code was generated by a tool. // Changes to this file may cause incorrect // behavior and will be lost if // the code is regenerated. // </autogenerated code> //------------------------------------------------------------ // // This source code was auto-generated // by WsContractFirst, Version=0.3.1.4244 namespace TestServices { using System.Diagnostics; using System.Xml.Serialization; using System; using System.Web.Services.Protocols; using System.ComponentModel; using System.Web.Services; /// <remarks></remarks> [System.Diagnostics.DebuggerStepThroughAttribute( )] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Web.Services.WebServiceBindingAttribute( Name="UserValidationSoap", Namespace="http://namespaces.infozerk.com/")] public class UserValidation : System.Web.Services.Protocols.SoapHttpClientProtocol { /// <remarks/> public UserValidation( ) { this.Url = "http://localhost/TestServices/UserValidation.asmx"; } /// <remark></remarks> [System.Web.Services.Protocols.SoapDocumentMethodAttribute(" http:/namespaces.infozerk.com/ValidateUser", RequestNamespace="http://namespaces.infozerk.com/", ResponseNamespace="http://namespaces.infozerk.com/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle= System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] public bool ValidateUser(int userID) { object[ ] results = this.Invoke("ValidateUser", new object[ ] {userID}); return ((bool)(results[0])); } /// <remarks/> public System.IAsyncResult BeginValidateUser( int userID, System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("ValidateUser", new object[ ] { userID}, callback, asyncState); } /// <remarks/> public bool EndValidateUser(System.IAsyncResult asyncResult) { object[ ] results = this.EndInvoke(asyncResult); return ((bool)(results[0])); } } }
You will then be able to use this class from your project to interface with the UserValidation Web Service.
13.9.3. ContractFirst Options
The ContractFirst add-in also contains a number of options and properties that can be used to customize the code that is generated by the tool. These options are shown in Table 13-1.
Table 13-1. ContractFirst add-in options
Name
Description
Public properties
If checked, the add-in will use public properties instead of using public fields.
Serializable classes
If checked, the add-in will create serializable data classes.
Collection
If checked, the add-in will generate collections to use instead of arrays.
Endpoint URL configuration
If checked, endpoint configuration in the .config file will be enabled.
Access to SOAP messages
If checked, read-only access to the SOAP messages will be added.
Validate messages
If checked, the add-in will enable XSD-based validation on the incoming SOAP messages.
The ContractFirst add-in is a great tool if you have subscribed to the idea of contract-first Web Service development. There are also plans in the future to add a wizard that would help you create WSDL files starting with an XSD file so you don't have to worry about all the clumsy WSDL details. Instead, ContractFirst will present you with a simple but powerful abstraction of it. Look for a lot more from this tool in the future.