In the last weeks I faced a real world example of an ASMX to WCF migration. The existing services has been relying in many of the features available in ASMX, and a complete rewrite would not be an option.
Kenny Wolf describes in a post how to reuse an implementation of an ASMX Web Service, "double-decorating" the classes to support both ASMX and WCF with the same sources.
Kirk Allen Evans extends that idea allowing the clients remain unchanged in a first phase. That means the web services' URL remain with the same .asmx extension replacing the ASP.NET build provider:
<buildProviders> <remove extension=".asmx"/> <add extension=".asmx" type="System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/> </buildProviders>
Based on my experience, I can add some comments:
1. Action names
The default action names that WCF assigns for each operation is different than ASMX ones. ASMX uses the namespace appended by the message name, while WCF uses the namespace, the service name and the operation name. This means that if you want to be fully compatible with old clients, you have to specify the Action
name:
[WebService(Namespace = "http://mynamespace/")] [ServiceContract(Namespace = "http://mynamespace/")] public class MyService { [WebMethod] [OperationContract(Action = "http://mynamespace/TheWebMethod")] public void TheWebMethod() { // . . . } }
2. Message names
If you used the MessageName
argument for a web method, then you have to specify the corresponding Name
argument in the operation contract. Also, the action name must be modified:
[WebService(Namespace = "http://mynamespace/")] [ServiceContract(Namespace = "http://mynamespace/")] public class MyService { [WebMethod(MessageName = "MyWebMethod")] [OperationContract(Action = "http://mynamespace/MyWebMethod", Name = "MyWebMethod")] public void TheWebMethod() { // . . . } }
3. Xml serialization
In the most common cases you also created your own "data contracts" for ASMX, creating Xml serializable clases. In order to support the same serialization you have to add the XmlSerializerFormat
attribute:
[WebService(Namespace = "http://mynamespace/")] [ServiceContract(Namespace = "http://mynamespace/")] [XmlSerializerAttribute] public class MyService { [WebMethod] [OperationContract(Action = "http://mynamespace/TheWebMethod")] public MyDataType TheWebMethod() { // . . . } }
4. ASP.NET compatibility
In those cases where you used ASP.NET specific features, or just for example want to have access to the current HttpContext
and don't want to change all the source code to make it in the WCF way, you can take advantage of the ASP.NET compatibility mode of WCF.
<system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <system.serviceModel>
The problem with this mode, is that if you replaced the .asmx build provider as I mention before and pointed by Kirk your services will fail miserably with an error like this:
Unable to cast object of type 'System.Web.Compilation.BuildResultCustomString' to type 'System.Web.Compilation.BuildResultCompiledType'.
Fortunately, there is a very simple solution for this. Just replace the .asmx HTTP handler with the WCF one:
<httpHandlers> <remove path=".asmx" verb="*" /> <add path="*.asmx" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false" /> </httpHandlers>
There are also some other important points to take into account in order to be fully compatible with old clients. For example, ASMX allows thin clients to invoke services using HTTP GET and/or PUT request with arguments encoded in the query string and the request body respectively. There are some improvements for this in Orcas that would support this out of the box, but in order to be fully compatible, all this should be supported by the same endpoint at the same time without forgetting the SOAP protocol. I had to deal with this also, but that gives enough material for a future post :).