Using WCF, JSON, LINQ, and AJAX: Passing Complex Types to WCF Services with JSON Encoding

In my previous post, I showed how to use the DataContractSerializer with the classes generated by the LINQ to SQL designer.  As a refresher, here's the service.  It is a trouble ticket lookup service based on the 3 parts of a phone number (referred to as an Ani) which follows the format (NPA)NXX-LINE:

 using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Linq;


[ServiceContract(Name="TroubleTicketService", Namespace="https://blogs.msdn.com/kaevans")]
public interface IService
{
    [OperationContract] 
    IEnumerable<TroubleTicket> GetTroubleTickets(Ani tn);
}

[DataContract(Namespace = "https://blogs.msdn.com/kaevans")]
public class Ani
{
    [DataMember]
    public string NPA { get; set; }
    [DataMember]
    public string NXX { get; set; }
    [DataMember]
    public string Line { get; set; }
}
public class Service : IService
{
    public IEnumerable<TroubleTicket> GetTroubleTickets(Ani tn)
    {
        TroubleTicketsDataContext db = new TroubleTicketsDataContext(@"Server=(local);Integrated Security=true;Initial Catalog=tickets");
        var tickets = from t in db.GetTroubleTickets(tn.NPA, tn.NXX, tn.Line)                      
                      select  t;
        return tickets;   
    }
}

Here's the LINQ to SQL Designer image overlayed with the Server Explorer pane.  I have a simple table, TroubleTicket, that has a stored procedure, GetTroubleTickets, which accept 3 string parameters.  Since we are using this with WCF, make sure to click the designer surface and show the Property Pane to change the "Serialization Mode" property value to "Unidirectional".

Linq to SQL with Stored Procedures 

You can see that there's nothing in there that says anything about JSON support, yet we can configure this service to return JSON over HTTP instead of SOAP.  .NET 3.5 introduces JSON serialization support for services without having to decorate your code with any special attributes, you just configure an endpoint to use the webHttpBinding, and add an endpoint behavior that enables web scripting.

   <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="AjaxBehavior">
          <enableWebScript />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="Service">
        <endpoint 
          address="" 
          binding="webHttpBinding"
          contract="IService" 
          behaviorConfiguration="AjaxBehavior" />        
      </service>
    </services>
  </system.serviceModel>

Very cool, now our service is exposed and returns JSON over HTTP instead of XML over HTTP!  You can now go to your service endpoint and append "/js" to the querystring to see the JSON proxy that is generated.  For instance, the endpoint URL on my machine is:

https://localhost:49455/WCFServices/Service.svc/js

To call the service, ASP.NET 3.5's built-in AJAX support makes referencing the JavaScript proxy very simple.

     <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Path="~/Scripts.js" />
        </Scripts>
        <Services>
            <asp:ServiceReference Path="~/Service.svc" />
        </Services>
    </asp:ScriptManager>

In this example, I am using the ScriptManager control in ASP.NET and pointing to our Service.svc host file.  Thanks to the JavaScript Intellisense features in Visual Studio 2008, I get Intellisense completion so that I can figure out how to reference the proxy correctly.

JavaScript Intellisense for Service Proxies in Visual Studio 2008

The question is, how do you call it?  You will see a ton of blog posts that show how to process the result of a web service call, even how to pass simple types as parameters to a web service proxy, but how do you pass a complex type?  The problem was that I could not figure out how to cons up an Ani type.  It's shown in the Intellisense, but there are no properties defined for it, and you can't use a "new" operator in JavaScript.  After a bit of searching the web, it turns out that calling JSON services looks a whole lot like the object construction syntax introduced in C# 3.0.

 function callService(npa, nxx, line)
{        
    var tn = {"NPA" : npa, "NXX" : nxx, "Line" : line};        
    blogs.msdn.com.kaevans.TroubleTicketService.GetTroubleTickets(tn,onServiceCompleted);                   
}

Inside the callService method, we create an Ani class using JSON.  To do that, we create a variable called "tn" that has 3 properties: NPA, NXX, and Line, and we provide the values that were passed into the method.  Then just pass that to the method, and you get your result.

Here is the complete .ASPX source.  There is no code-behind for this page because we are doing all of the processing client-side.

 <%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="https://www.w3.org/1999/xhtml">
<head runat="server">
    <title>WCF and AJAX Demo</title>    
    <link href="StyleSheet.css" rel="stylesheet" type="text/css" />    
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Path="~/Scripts.js" />
        </Scripts>
        <Services>
            <asp:ServiceReference Path="~/Service.svc" />
        </Services>
    </asp:ScriptManager>        
        <div id="query">
            <asp:TextBox ID="npa" runat="server"></asp:TextBox>
            <asp:TextBox ID="nxx" runat="server"></asp:TextBox>
            <asp:TextBox ID="line" runat="server"></asp:TextBox>
            <input type="button" id="button" value="Call Service" onclick="return onClick();" />
        </div>
        <div id="results">
            <asp:Table ID="resultTable" runat="server" EnableViewState="false" Width="100%">
                <asp:TableHeaderRow>
                    <asp:TableHeaderCell>ID</asp:TableHeaderCell>
                    <asp:TableHeaderCell>NPA</asp:TableHeaderCell>
                    <asp:TableHeaderCell>NXX</asp:TableHeaderCell>
                    <asp:TableHeaderCell>LINE</asp:TableHeaderCell>
                    <asp:TableHeaderCell>Description</asp:TableHeaderCell>                
                </asp:TableHeaderRow>        
            </asp:Table>
        </div>    
    </form>
</body>
</html>

The UI is very simple, we have a couple UI elements that gather user input and a button with a JavaScript click handler.  The work is done within our JavaScript (scripts.js, referenced in the ScriptManager control).  That code is also simple, we just call the service and then add rows to the HTML table with the results.

 function onClick()
{
    callService($get("npa").value, $get("nxx").value, $get("line").value);
}

function callService(npa, nxx, line)
{        
    var tn = {"NPA" : npa, "NXX" : nxx, "Line" : line};        
    blogs.msdn.com.kaevans.TroubleTicketService.GetTroubleTickets(tn,onServiceCompleted);                   
}

function onServiceCompleted(sender, e)
{
   if(sender.length > 0)
   {
      for(var i=0;i<sender.length;i++)
      {          
          var ticket = sender[i];
          addTableRow(ticket.TicketID, ticket.NPA, ticket.NXX, ticket.Line, ticket.Description, ticket.Status);
      }
   }
}

$ctn = function(text){return document.createTextNode(text);}

function addTableRow(id, npa, nxx, line, description, status)
{
    var table = $get("resultTable"); 

    var row = table.insertRow(table.rows.length);
    var idCell = row.insertCell(0);
    idCell.appendChild($ctn(id));

    var npaCell = row.insertCell(1);
    npaCell.appendChild($ctn(npa));

    var nxxCell = row.insertCell(2);
    nxxCell.appendChild($ctn(nxx));

    var lineCell = row.insertCell(3);
    lineCell.appendChild($ctn(line));

    var descriptionCell = row.insertCell(4);
    descriptionCell.appendChild($ctn(description));

    var statusCell = row.insertCell(5);
    statusCell.appendChild($ctn(status));        
}

If you haven't seen the "$get" shortcut method for ASP.NET AJAX, it is just a global function defintion that is equivalent to document.getElementById(name).  I just use that to query the UI elements for their values and then pass into my callService function.  Notice that I created a shortcut method "$ctn" that replaces the createTextNode functionality, it works the same way. 

That's all there really is to using ASP.NET AJAX and WCF together.  The LINQ inclusion was just icing on the cake.  In all, this took me MUCH less time to develop than if I had tried to use SOAP or REST/POX and process the XML result on the client.  Using JSON encoding significantly shortened my development cycle, and I really didn't have to know anything about JSON except for how to create the class variable to pass to my service.  And the Intellisense in Visual Studio 2008 Beta 2 makes this so much easier to work with.

Man, I think I am getting to be more of a fan of JSON services instead of SOAP based services!  Never thought I would say that.

For more information on the technologies used in this post, here are some great references: