How to: Migrate AJAX-Enabled ASP.NET Web Services to WCF
This topic outlines procedures to migrate a basic ASP.NET AJAX service to an equivalent AJAX-enabled Windows Communication Foundation (WCF) service. It shows how to create a functionally equivalent WCF version of an ASP.NET AJAX service. The two services can then be used side by side, or the WCF service can be used to replace the ASP.NET AJAX service.
Migrating an existing ASP.NET AJAX service to a WCF AJAX service gives you the following benefits:
You can expose your AJAX service as a SOAP service with minimal extra configuration.
You can benefit from WCF features such as tracing, and so on.
The following procedures assume that you are using Visual Studio 2010.
The code that results from the procedures outlined in this topic is provided in the example following the procedures.
For more information about exposing a WCF service through an AJAX-enabled endpoint, see the How to: Use Configuration to Add an ASP.NET AJAX Endpoint topic.
To create and test the ASP.NET Web service application
Open Visual Studio 2010.
From the File menu, select New, then Project, then Web, and then select ASP.NET Web Service Application.
Name the project ASPHello and click OK.
Uncomment the line in the Service1.asmx.cs file that contains
System.Web.Script.Services.ScriptService]
to enable AJAX for this service.From the Build menu, select Build Solution.
From the Debug menu, select Start Without Debugging.
On the Web page generated, select the
HelloWorld
operation.Click the Invoke button on the
HelloWorld
test page. You should receive the following XML response.<?xml version="1.0" encoding="utf-8" ?> <string xmlns="http://tempuri.org/">Hello World</string>
This response confirms that you now have a functioning ASP.NET AJAX service and, in particular, that the service has now exposed an endpoint at Service1.asmx/HelloWorld that responds to HTTP POST requests and returns XML.
For more information about how to access this service see Using Web Services in ASP.NET AJAX.
Now you are ready to convert this service to use a WCF AJAX service.
To create an equivalent WCF AJAX service application
Right-click the ASPHello project and select Add, then New Item, and then AJAX-enabled WCF Service.
Name the service WCFHello and click Add.
Open the WCFHello.svc.cs file.
From Service1.asmx.cs, copy the following implementation of the HelloWorld operation.
public string HelloWorld() { return "Hello World"; }
Paste to copied implementation of the HelloWorld operation into the WCFHello.svc.cs file in place of the following code.
public void DoWork() { // Add your operation implementation here return; }
Specify the Namespace attribute for ServiceContractAttribute as WCFHello.
[ServiceContract(Namespace="WCFHello")] [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)] public class WCFHello { … }
Add the WebInvokeAttribute to the HelloWorld operation and set the RequestFormat property to return Xml. Note that, if not set, the default return type is Json.
[OperationContract] [WebInvoke(ResponseFormat=WebMessageFormat.Xml)] public string HelloWorld() { return "Hello World"; }
From the Build menu, select Build Solution.
Open the WCFHello.svc file and from the Debug menu, select Start Without Debugging.
The service now exposes an endpoint at WCFHello.svc/HelloWorld, which responds to HTTP POST requests. HTTP POST requests cannot be tested from the browser, but the endpoint returns XML following XML.
<string xmlns="https://schemas.microsoft.com/2003/10/Serialization/">Hello World</string>
The WCFHello.svc/HelloWorld and the Service1.aspx/HelloWorld endpoints are now functionally equivalent.
Example
The code that results from the procedures outlined in this topic is provided in the following example.
//This is the ASP.NET code in the Service1.asmx.cs file.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using System.Web.Script.Services;
namespace ASPHello
{
/// <summary>
/// Summary description for Service1.
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
}
}
//This is the WCF code in the WCFHello.svc.cs file.
using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
namespace ASPHello
{
[ServiceContract(Namespace = "WCFHello")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class WCFHello
{
// Add [WebInvoke] attribute to use HTTP GET.
[OperationContract]
[WebInvoke(ResponseFormat=WebMessageFormat.Xml)]
public string HelloWorld()
{
return "Hello World";
}
// Add more operations here and mark them with [OperationContract].
}
}
The XmlDocument type is not supported by the DataContractJsonSerializer because it is not serializable by the XmlSerializer. You can use either an XDocument type, or serialize the DocumentElement instead.
If ASMX Web services are being upgraded and migrated side-by-side to WCF services, avoid mapping two types to the same name on the client. This causes an exception in serializers if the same type is used in a WebMethodAttribute and a ServiceContractAttribute:
If WCF service is added first, invoking the method on ASMX Web Service causes exception in ConvertValue because the WCF style definition of the order in the proxy takes precedence.
If ASMX Web Service is added first, invoking method on WCF service causes exception in DataContractJsonSerializer because the Web Service style definition of the order in the proxy takes precedence.
There are significant differences in behavior between the DataContractJsonSerializer and the ASP.NET AJAX JavascriptSerializer. For example, the DataContractJsonSerializer represents a dictionary as an array of key/value pairs, whereas the ASP.NET AJAX JavascriptSerializer represents a dictionary as actual JSON objects. So the following is the dictionary represented in ASP.NET AJAX.
Dictionary<string, int> d = new Dictionary<string, int>();
d.Add(“one”, 1);
d.Add(“two”, 2);
This dictionary is represented in JSON objects as shown in the following list:
[{"Key":"one","Value":1},{"Key":"two","Value":2}] by the DataContractJsonSerializer
{“one”:1,”two”:2} by the ASP.NET AJAX JavascriptSerializer
The DataContractJsonSerializer is more powerful in the sense that it can handle dictionaries where the key type is not string, while the JavascriptSerializer cannot. But the latter is more JSON-friendly.
The significant differences between these serializers are summarized in the following table.
Category of Differences | DataContractJsonSerializer | ASP.NET AJAX JavaScriptSerializer |
---|---|---|
Deserializing the empty buffer (new byte[0]) into Object (or Uri, or some other classes). |
SerializationException |
null |
Serialization of Value |
{} (or {"__type":"#System"}) |
Null |
Serialization of the private members of [Serializable] types. |
serialized |
not serialized |
Serialization of the public properties of ISerializable types. |
not serialized |
serialized |
"Extensions" of JSON |
Adheres to the JSON specification, which requires quotes on object member names ({"a":"hello"}). |
Supports the names of object members without quotes ({a:"hello"}). |
DateTime Coordinated Universal Time (UTC) |
Does not support format "\/Date(123456789U)\/" or "\/Date\(\d+(U|(\+\-[\d{4}]))?\)\\/)". |
Supports format "\/Date(123456789U)\/" and "\/Date\(\d+(U|(\+\-[\d{4}]))?\)\\/)" as DateTime values. |
Representation of dictionaries |
An array of KeyValuePair<K,V>, handles key types that are not strings. |
As actual JSON objects - but only handles key types that are strings. |
Escaped characters |
Always with an escape forward slash (/); never allows un-escaped invalid JSON characters, such as "\n". |
With an escape forward slash (/) for DateTime values. |