Evaluate Weigh the pros and cons of technologies, products and projects you are considering.

Checking a Web service for version changes at runtime

.NET Technologies expert Jim Culbert addresses a method for checking an ASP.NET Web service.

A SearchWebServices.com member posed the following question to .NET Technologies Expert Jim Culbert: I have created...

a Web service that is referenced in Winforms. At runtime, I need to check if there is any change in the ASP.NET Web service, and if so, update the client side reference. Please let me know a way to do this programmatically. Any URI's or resource centers would be of real help.

Here is Jim's answer:
Your question illuminates a current challenge for Web services developers. Specifically, once a Web service is "published" how do we deal with changes? The current tools and specifications provide little support for versioning. Even more challenging, since there are only a limited number of non-trivial Web services projects running today, there are few, tested best-practices around Web services operational issues such as versioning. So with that glum introduction, here is what I can tell you.

First, the problem of interface versioning is not unique to Web services. Any software that must integrate with other software does so through an interface. Interfaces range from function call interfaces, to file interfaces to ports on the network that perform specific tasks. Once you have an interface, you typically need a way to describe this interface to the outside world so other folks can interact with your software. This is done by separating the description of the interface from the implementation of the interface. The most common example is the C/C++ header file that contains the prototypes for classes, methods and functions that a developer creates. These files tell other implementers and their tools (e.g., the compiler) how to interact with the developer's compiled code libraries.

Interface files such as the C/C++ header are said to represent a "contract" with the outside world. It's a promise by the developer of a code library, for instance, that if other developers follow the contract, then the code will work. Break the contract and all bets are off. Interface contracts are a great invention in that they allow a developer to make all the changes she wants to her code without impacting users of her code as long as she honors the contract she publishes. The problem comes when your users have one version of the contract and you want to create a new one that doesn't follow the rules in the old one. Changing a function's prototype of the members of a class are examples of changes to an original header file that would break your original contract.

While you try very hard from release to release not to break your interface contracts there are lots of times when you just can't avoid this. In this case we create a new version of the interface. Along with the new version, you need to publish a new contract (header, IDL, WSDL file) and that contract must contain information that identifies it as a new version of the interface. So let's now get Web services specific.

In Web services, your contract with the world is your WSDL file. This file describes what requests and responses you're capable of handling as well as XML message formats that are passed between the caller and the service. Your service interface is implemented in .Net as an asmx file located at a specific URL. Now, when you change the software behind the asmx file, you have two options. You can treat the change as constituting a brand new interface implementation and consequently dedicate a brand new URL/asmx pair to that service. At that location, you would publish a brand new WSDL and it would be clear to clients that this is a different WSDL from the original as its name, location, namespace etc. would be completely different from the original WSDL. Your other option is to publish a new WSDL file alongside the old one at the original location. The new WSDL file would reflect the changes you made to the service. The second WSDL indicates to the outside world that your service honors two contracts. Now, I know you're thinking "but I don't want two WSDLs I just want to update the WSDL file and change its version somehow". In fact, whether you have two WSDLs or one, the versioning issues are roughly the same. So let's talk about a very specific case where you want to keep one WSDL, you want to modify it and you want to reflect the modification as a change to the WSDL version and you're going to do this in .NET. There are two sub scenarios as well. In one, your changes are all backward compatible (you add a new web method or new XML message type). In the other, you break your contract by changing an existing web method or changing a message type.

In both cases, your main mechanism for versioning is the "targetNamespace" for the WSDL file. This the accepted practice for versioning XML schema and the recommended practice for how to version WSDL files. So now your thinking "well why didn't you tell me that in the first place instead of blathering on about contracts and such". The reason is that it's not that easy. If all you do is change the targetNamespace for your WSDL then recompile your Web service, none of your existing clients using the old WSDL version will be able to use your service even if you have not changed anything. The reason is that when your client compiles its proxy stubs that interact with the Web service for you, it takes into account the target namespace for the WSDL file. This namespace is used to qualify messages and SOAP calls. Thus, your old clients will be chatting using the old namespace qualifiers and your newly compiled service will be expecting SOAP calls and messages to be qualified in the old namespace. Your service will not recognize the SOAP calls or the data!! There are two solutions to remedy this. The brute-force method is to copy the service and deploy it on a new URL. Old clients will access the old URL that supports the old namespace and the new clients will use the new URL and the new namespace. This is obviously ugly and difficult to maintain as you are copying code and creating new sites.

If you want to upgrade the version (in a backwards compatible way!) and keep it on the same URL and the same code behind, then you need to do some WSDL tinkering. There is a good article that describes how to do this here. The main rub is that the target namespace of the WSDL file is also used to indicate the target namespace for the message definitions in the file and to create the SOAP action portion of the message sent to the Web service. The SOAP action is used by the .NET framework to route your request to the code that handles the request. The action is created by concatenating the target namespace to the name of the method. Thus, a target namespace of http://www.culbert.net/asktheexpert/sample/v1/ and a method name of GetQuote would generate a SOAP action element of http://www.culbert.net/asktheexpert/sample/v1/GetQuote. This selects the function to execute when the Web service receives the SOAP message. Obviously if I change the WSDL file to have a target namespace that bumps v1 to v2, then my SOAP action will change to http://www.culbert.net/asktheexpert/sample/v2/GetQuote. This is the action that will now be expected by the service for the GetQuote method. All the old clients will be sending actions with v1 in them and your service will act as if it has never heard of such a thing. Likewise, the target namespace for the messages defined in the file will be different from the namespace that the old clients will present along with their message instances and your service will not recognize the data. To fix this, you have to make some specific tweaks to your code.

As a first step, you can go into your source and apply attributes to each of your Web methods. Specifically, you can set the soap action for each method. This fixes your SOAP action problem because you can go into your source and add attributes to each web method from v1 with a v1 action and each from v2 with a v2 action. The WSDL generated will appropriately reflect the correct actions. In a similar fashion, you can specify attributes that tell the code generator and the framework which namespace the request and response messages should reside in. Here is an exemplar class.

 //This sets the targetNamespace for the WSDL document [WebServiceAttribute(Namespace = http://jim.org/v2)] public class Service1 : System.Web.Services.WebService { public Service1() { InitializeComponent(); } [WebMethod] //This sets the sets the SOAP action and the request/response namespaces. //Note they're set to the v1 URI [SoapDocumentMethodAttribute( Action="http://jim.org/v1/GetQuote", RequestNamespace= http://jim.org/v1, ResponseNamespace= http://jim.org/v1)] public decimal GetQuote(String theCompany) { decimal tmp = (decimal)Application["TheQuote"]; return tmp; } [WebMethod] [SoapDocumentMethodAttribute( Action="http://jim.org/v1/SetQuote", RequestNamespace= http://jim.org/v1, ResponseNamespace= http://jim.org/v1)] public void SetQuote(string theCompany, decimal newQuote) { Application["TheQuote"] = newQuote; } //Note that we do not set SOAP document information here. This method //will adopt the default v2 namespace for both the action and request/response //schema namespaces. [WebMethod] public string SayHello(){ return "Hello";} }

This code will generate a service that is backward compatible with clients created against v1 WSDL while supporting new clients who need the "SayHello" method as defined in the v2 version.

That's enough to ingest for now. I will follow up with an article that talks more about how to logically separate new version code into new interfaces when you decide you need to change a method signature or data format (break your contract with existing clients).

Dig Deeper on Topics Archive

Start the conversation

Send me notifications when other members comment.

Please create a username to comment.