Blog

  • Home /
  • Blog /
  • Migrating a Versionable ASMX Web Service to WCF

Migrating a Versionable ASMX Web Service to WCF

December 21, 2008

Creating a versionable ASMX Web Service is something that was really hard to do in .NET 1.1, mostly because it involved a lot of work and discipline. Creating versionable services has become quite easy with WCF because this is an out-of-the-box feature. But what about those web services you already created and that are being used by possibly dozens of applications? Are you stuck with those pesky ASMX Web services or is it possible to easily move them to WCF without much effort? As it turns out, you can replace your old ASMX web services with WCF even without the need to change or recompile the client software applications.

First, let’s talk about how I’ve been developing versionable ASMX web services in the past. After that, I’ll show you how to easily migrate an ASMX web service to WCF.

Versionable ASMX Web Services in .NET 1.1

For building versionable ASMX web services, I’ve been using XML messages with a version number that indicates the particular edition of a message. These XML messages are composed by a service agent component. This service agent component is responsible for providing a strongly type interface to the using applications and mapping these to their respective XML representation. The service agent then makes a call to the ASMX web service after which it translates/validates the received XML response back to a strongly typed representation.

image

When the ASMX web service receives a message from a service agent, it first extracts the version number and then sends it to an appropriate message handler for that particular version. The message is then translated back to an object representation after which the requested action is executed.

I agree that this is a lot of work, but it turned out very well in a .NET 1.1 environment. It adds the tremendous benefit of being able to change the contract (= new version) of the ASMX web service without the need to change any of the client applications. Regression tests are certainly desirable before releasing a new version in order to check whether the most recent changes didn’t break anything for the older contracts.

This simple code sample illustrates how a service agent might work:  

public class ServiceAgent
{
    private readonly ServiceCredentials _serviceCredentials;
 
    public ServiceAgent(ServiceCredentials serviceCredentials)
    {
        _serviceCredentials = serviceCredentials;
    }
 
    public ProcessingResult ProcessOrder(Order order)
    {
        OrderXmlRequestMapper xmlRequestMapper = 
            new OrderXmlRequestMapper();
        String xmlRequest = xmlRequestMapper.MapFrom(order);
 
        String xmlResponse = String.Empty;
        using(AsmxService service = ServiceProxyFactory.
            CreateServiceProxy(typeof(AsmxService), 
                               _serviceCredentials))
        {
            xmlResponse = service.ProcessOrder(xmlRequest);
        }
 
        ProcessingResultXmlResponseMapper xmlResponseMapper = 
            new ProcessingResultXmlResponseMapper();
        ProcessingResult result = 
            xmlResponseMapper.MapFrom(xmlResponse);
 
        return result;
    }
}

And this is some sample code for an ASMX web service:

[WebService(Namespace = http://www.jvr.be/AsmxService)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AsmxService : WebService
{
    [WebMethod(MessageName = "ProcessOrder")]
    public String ProcessOrder(String xmlRequest)
    {
        // Convert the XML request back to objects
 
        // Process the order
        Debug.Write("ProcessOrder on ASMX service called.");
 
        // Return an XML response
        String xmlResponse = "This is a versioned reponse.";
        return xmlResponse;
    }
}

Notice how dutifully a meaningful namespace for the web service and a message name for the web method is provided. This is something that I always considered a best practice. Turns out that it does have some benefits.

Anyway, enough is enough. Let’s see how we can replace such an ASMX web service with a WCF service.

Migrating from ASMX web services to WCF

When migrating to a WCF service, we obviously want the existing client applications to keep working with the service agents. New applications can use a WCF client proxy for communicating with a new contract of the WCF service. This way we can use the versioning technology of WCF for future releases. Existing applications can gradually move to a WCF client proxy as well, but at their own pace.

The whole setup is to trick the service agents that the ASMX web service is still there while it actually is replaced with a WCF service that provides an extra service contract that mimics the old ASMX web service for backwards compatibility. Let’s see how we can do this in code.

First we define a service contract for the old ASMX web service.

[ServiceContract(Namespace = http://www.jvr.be/AsmxService)]
public interface IOldAsmxService
{
    [OperationContract(
        Action = http://www.jvr.be/AsmxService/ProcessOrder)]
    String ProcessOrder(String xmlRequest);
}

Notice the namespace provided by the ServiceContract and the action for the OperationContract. This is how the new service contract looks like:

[ServiceContract]
public interface IWcfService
{
    [OperationContract]
    ProcessOrderResponse ProcessOrder(
        ProcessOrderRequest request);
}

Implementing the concrete service class is somewhat straightforward:

[ServiceBehavior(Namespace = http://www.jvr.be/AsmxService)]
public class WcfService : IWcfService, IOldAsmxService
{
    public ProcessOrderResponse ProcessOrder(
        ProcessOrderRequest request)
    {
        // Process the order
        Debug.Write("ProcessOrder of IWcfService called.");
 
        return new ProcessOrderResponse();
    }
 
    public String ProcessOrder(String xmlRequest)
    {
        // Map XML request to a ProcessOrderRequest 
        // (in a separate mapper class!!)
 
        // Process the order
        Debug.Write("ProcessOrder of IOldAsmxService called.");
        var response = ProcessOrder(new ProcessOrderRequest());
 
        // Map ProcessOrderResponse to a XML response 
        // (in a separate mapper class!!)
        return "Some mapped XML reponse";
    }
}

Notice that the service method that supports the contract of the old ASMX web service delegates its call to to the new method. You can also do this the other way around if you want.

The first step to make this all work is to add a new file with an .asmx extension that contains the following line:

<%@ ServiceHost language=c# Debug=”true” Service=”Jvr.WcfService” %%>

The next step is to add the following configuration settings to the web.config file of the WCF service.

<system.web>
    <compilation debug="false">
        <buildProviders>
            <remove extension=".asmx"/>
            <add extension=".asmx" 
        type="System.ServiceModel.Activation.ServiceBuildProvider, 
        System.ServiceModel, Version=3.0.0.0, Culture=neutral, 
        PublicKeyToken=b77a5c561934e089" />
        </buildProviders>
    </compilation>
</system.web>

Make sure to create an endpoint for both the old and the new service contracts. You have to use basicHttpBinding for the old service contract. Now you can replace your old ASMX service with a new shiny WCF service and this without breaking any client applications.

Thoughts? Flames? Anything? Please let me know.

Profile picture of Jan Van Ryswyck

Jan Van Ryswyck

Thank you for visiting my blog. I’m a professional software developer since Y2K. A blogger since Y2K+5. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.

Comments

About

Thank you for visiting my website. I’m a professional software developer since Y2K. A blogger since Y2K+5. Curator of the Awesome Talks list. Past organizer of the European Virtual ALT.NET meetings. Thinking and learning about all kinds of technologies since forever.

Contact information

(+32) 496 38 00 82

infonull@nullprincipal-itnull.be