Saturday, October 20, 2012

Querying Multiple Databases in One LINQ Expression


Introduction

The objective of this article is to demonstrate functionality in the LinqToSql library that transforms LINQ expression trees to SQL statements that can be executed against multiple RDMS systems and not just Microsoft's SQL Server offerings. The LinqToSql library implements the following features and functionality:
  1. Queries multiple databases in one expression e.g. a Microsoft Access Database and an SQL Server Database
  2. Translates function calls and property accessors in the String and DateTime classes that have SQL equivalents e.g. firstName.LengthfirstName.ToUpper()orderDate.Year etc.
  3. Implements all IQueryable methods e.g. GroupByAnyAllSumAverage, etc.
  4. Correctly and comprehensively translates binary and unary expressions that have valid translations into SQL.
  5. Parameterizes queries instead of embedding constants in the SQL transformation.
  6. Performs caching of previously translated expression trees.
  7. Does not use MARS - Multiple Active Result Sets, an SQL Server 2005 specific feature.
  8. Correctly translates calls to SelectMany even when the query source involves method calls. The SQL Server 2005 specific keyword CROSS APPLY is neither required nor used.
So far, I have tested the functionality on SQL Server 2000 and 2005. Testing for compatibility with Microsoft Access and other RDMSs is underway as the project is still under active development.
The project file available above for download contains samples that run against the famous Northwind database on both SQL Server 2005 and Microsoft Access.
For implementation details, please see the following articles 12, 3.
In this article, I will focus on the Join functionality and the use of function calls and property accessors in theString and DateTime classes. But first...

Querying Multiple Databases in One LINQ Expression

Scenario

The Northwind corporation is facing financial meltdown as a result of its incompetent customer relations and inept order fulfilment. In order to save the situation, Northwind's management sent out a questionnaire asking its customers what their favourite movie is. The plan is to determine who the director of that movie is and to then send the customer a DVD collection of other films by that director. Unfortunately only one customer responds to the questionnaire.
Nevertheless the query must go on... against the following:
  1. Northwind DB on SQL Server
  2. FavouriteDVD.mdb, a Microsoft Access File
  3. An in-memory data structure with directors and a list of movies they've directed
We set up query sources as follows:
customers = new Query<customer />(new SqlQueryProvider(northwindConnection));
// northwindConnection is an SqlConnection to Northwind Db on SQL Server
.............

favouriteDVDs = new Query<favouritedvd />(new SqlQueryProvider(favouriteConnection));

// favouriteConnection is an OleDbConnection to FavouriteDVD.mdb
..................................................
var directorMovies = new Dictionary<string, />>();

directorMovies.Add("Stanley Kubrick", new List<string />() 
    { "Paths of Glory", "A Clockwork Orange" });
directorMovies.Add("Alfred Hitchcok", new List<string />() 
    { "39 Steps", "The Lady Vanishes" });
directorMovies.Add("John Frankenheimer", new List<string />() 
    { "Ronin" });
Now that we've set up our data sources, we can run the query:
var dvdInfo = (from customer in customers
               where customer.CustomerID == "ALFKI"
               select new {
                        customer.CustomerID,
                        customer.ContactName,
                        FavoriteDVD = (from favouriteDVD in favouriteDVDs
                                       where favouriteDVD.CustomerID == _
                            customer.CustomerID
                                       select new {
                                               Movie = favouriteDVD.DVDTitle,
                                               favouriteDVD.Director
                                      }).First()
                }
              ).ToList();
That gets us the customer information from Northwind (SQL Server) and the customer's favourite movie fromFavoriteDVD (Microsoft Access).
Now, we need a list of movies produced by the same director:
var dvdInfoPlus = from info in dvdInfo
                  select new {
                     info.CustomerID,
                     info.ContactName,
                     Movies = new {
                         info.FavoriteDVD,
                          MoviesBySameDirector = _
                           from movies in directorMovies[info.FavoriteDVD.Director]
                           select movies,
                                  }
                  };
The result is as follows:
CustomerID=ALFKI        ContactName=Maria Anders        Movies={ }
  Movies: FavoriteDVD={ }         MoviesBySameDirector=...
    FavoriteDVD: Movie=Apocalypse Now       Director=Stanley Kubrick
    MoviesBySameDirector: Paths of Glory
    MoviesBySameDirector: A Clockwork Orange
And there you have it. Download the sample code and try it out.

Aside

I did not purposely design LinqToSql to do this. I just stumbled on this functionality when I was running tests yesterday. I will explore this sort of thing in detail some other time. (Think querying and aggregating data from disparate databases, Web services, LDAP directories etc. using simple LINQ syntax. Interesting, eh?).

Fixing a Gaping Hole

If you downloaded the sample code last week and attempted to run a query like the one below, you would be out of luck:
var x = from c in customers
        join o in orders on c.CustomerID equals o.CustomerID
        let m = c.Phone
        orderby c.City
        where c.Country == "UK"
        where m != "555-555"
        select new { c.City, c.ContactName } into customerLite
        where customerLite.City == "London"
        select customerLite;

var y = x.ToList();
ObjectDumper.Write(y, 3);
A look at the expression generated will reveal why:
 .Join(, c => c.CustomerID, o => o.CustomerID, 
        (c, o) => new <>f__AnonymousType3`2(c = c, o = o))
 .Select(<>h__TransparentIdentifier46 => new <>f__AnonymousType14`2(
                           <>h__TransparentIdentifier46 = <>
                           h__TransparentIdentifier46, 
                          m = <>h__TransparentIdentifier46.c.Phone))
 .OrderBy(<>h__TransparentIdentifier47 => 
    <>h__TransparentIdentifier47.<>h__TransparentIdentifier46.c.City)
 .Where(<>h__TransparentIdentifier47 => 
    (<>h__TransparentIdentifier47.<>
        h__TransparentIdentifier46.c.Country = "UK"))
 .Where(<>h__TransparentIdentifier47 => 
     (<>h__TransparentIdentifier47.m != "555-555"))
 .Select(<>h__TransparentIdentifier47 => new <>f__AnonymousTypea`2(
   City = <>h__TransparentIdentifier47.<>
           h__TransparentIdentifier46.c.City, 
   ContactName = <>h__TransparentIdentifier47.<>
           h__TransparentIdentifier46.c.ContactName))
 .Where(customerLite => (customerLite.City = "London"))
This reveals at least the following issues:
  1. Very involved aliasing e.g.
    <>h__TransparentIdentifier47.<><br />h__TransparentIdentifier46.c.ContactName 
    which is aliasing a reference to c.ContactName
  2. Sequential calls to methods defined in the Queryable class e.g. .Where(...).Where(...)
In between bouts of heavy drinking over the weekend ;) I managed to resolve these issues. The specifics of how this was done will be addressed another day.
In any event, this expression and its ilk can now be parsed successfully. In this case, you'll get anSQLStatement that looks like so:
SELECT  City, ContactName
FROM 
Customers AS t1
 INNER JOIN 
Orders AS t2
 ON t1.CustomerID = t2.CustomerID
WHERE (t1.Country = @p2) AND (t1.Phone <> @p1) AND (t1.City = @p0)
ORDER BY t1.City 
This will produce the following result:
City=London     ContactName=Thomas Hardy
.............................................
City=London     ContactName=Victoria Ashworth
.............................................
City=London     ContactName=Elizabeth Brown
.............................................
City=London     ContactName=Ann Devon
.............................................
City=London     ContactName=Simon Crowther
.............................................
City=London     ContactName=Hari Kumar
.............................................

Samples and Translations

Example 1

var cutoffDate = new DateTime(1998, 1, 1);

var x = from c in customers
        join o in orders on c.CustomerID equals o.CustomerID
        where c.CustomerID.StartsWith("A") && o.OrderDate > cutoffDate
        orderby c.ContactName, o.OrderDate descending
        select new { Name = c.ContactName, o.OrderDate };

var y = x.ToList();
ObjectDumper.Write(y, 3);
The above query will produce a list of customers whose customerIDs begin with the letter "A" who placed orders after 1/1/1998. The following SQL query will be generated to produce the result:
SELECT  t1.ContactName, t2.OrderDate
FROM 
Customers AS t1
 INNER JOIN 
Orders AS t2
 ON t1.CustomerID = t2.CustomerID
WHERE (t1.CustomerID Like (@p1 + '%') AND (t2.OrderDate > @p0))
ORDER BY t1.ContactName , t2.OrderDate Desc
The results will look like so:
Name=Ana Trujillo       OrderDate=3/4/1998
Name=Antonio Moreno     OrderDate=1/28/1998
Name=Maria Anders       OrderDate=4/9/1998
Name=Maria Anders       OrderDate=3/16/1998
Name=Maria Anders       OrderDate=1/15/1998
Name=Thomas Hardy       OrderDate=4/10/1998
-----------------------------------------------------------------------------------

Example 2

var x = from c in customers
        orderby c.City
        where c.City.Contains("ri") || c.Country.EndsWith("o")
        select new { c.Country, c.City, c.ContactName };

var y = x.ToList();
ObjectDumper.Write(y, 3);
The above query will produce a list of customers who live in cities whose names contain the string "ri" or those who reside in countries where the country name ends with the letter "o".
SELECT  t0.Country, t0.City, t0.ContactName
FROM Customers AS t0
WHERE (t0.City Like ('%' + @p1 + '%') OR t0.Country Like ('%' + @p0))
ORDER BY t0.City 
The results will look like so:
Country=Venezuela       City=I. de Margarita    ContactName=Felipe Izquierdo
Country=Spain   City=Madrid     ContactName=Alejandra Camino
Country=Spain   City=Madrid     ContactName=Martín Sommer
Country=Spain   City=Madrid     ContactName=Diego Roel
Country=Mexico  City=México D.F.        ContactName=Francisco Chang
Country=Mexico  City=México D.F.        ContactName=Ana Trujillo

Example 3

var x = from order in orders
        where order.OrderDate.Value.Year > DateTime.Parse("1/1/1997").Year &&
              order.CustomerID.StartsWith("B")
        select new { order.CustomerID, order.OrderID, order.OrderDate };

ObjectDumper.Write(x);
This query will produce a list of customers who placed orders after 1997 and whose customerIDs start with the letter "B". The following SQL query will be generated to produce the result:
SELECT  t0.CustomerID, t0.OrderID, t0.OrderDate
FROM Orders AS t0
WHERE ((DATEPART(year, t0.OrderDate) > @p1) AND t0.CustomerID Like (@p0 + '%'))
The results will look like so:
CustomerID=BOTTM        OrderID=11048   OrderDate=4/24/1998
CustomerID=BLAUS        OrderID=11058   OrderDate=4/29/1998
CustomerID=BONAP        OrderID=11076   OrderDate=5/6/1998

Example 4

var x = customers
                .SelectMany(
                    c => orders.Where(o => c.CustomerID == o.CustomerID && 
                                           c.CustomerID.StartsWith("C")),
                    (c, o) => new { c.ContactName, o.OrderDate }
                )
                .OrderByDescending(d => d.OrderDate)
                .Select(e => new {e.ContactName, e.OrderDate})
                .Where(f => f.OrderDate > DateTime.Parse("1/1/1996"));

ObjectDumper.Write(x);
This query will produce a list of customers whose customerIDs start with the letter "C" who placed orders after 1/1/1996. The following SQL query will be generated to produce the result:
SELECT  ContactName, OrderDate
FROM 
Customers as t1
 , as t2
Orders
WHERE ((t1.CustomerID = Orders.CustomerID) AND _
        t2.CustomerID Like (@p1 + '%')) AND (t2.OrderDate > @p0)
ORDER BY OrderDate Desc
The results will look like so:
ContactName=Patricio Simpson    OrderDate=4/28/1998
ContactName=Yang Wang   OrderDate=4/22/1998
ContactName=Pedro Afonso        OrderDate=4/22/1998
ContactName=Yang Wang   OrderDate=4/16/1998
ContactName=Pedro Afonso        OrderDate=3/23/1998
ContactName=Yang Wang   OrderDate=3/20/1998
ContactName=Patricio Simpson    OrderDate=3/10/1998 
.................................................................................
That's it for now. In the next article, I will investigate how the performance of LinqToSql squares up against DLINQ.

Notes

  1. All 40 or so samples in the download will run successfully on Microsoft Access and SQL Server, except for those that have multiple parameters - these will not return results when run against Microsoft Access. I am investigating the cause of this behaviour.
  2. As a quick fix to get the samples that call SelectMany working on both Microsoft Access and SQL Server, the CrossJoinHandler will not emit the CROSS JOIN keyword, instead tables are separated by a comma. This is not optimal behaviour and the more elaborate fix alluded to in the previous article will be applied later.
  3. MARS is not used but multiple simultaneous connections may be opened during query evaluation. I will detail when and why this happens, performance implications and possible workarounds in another article.
  4. Most functionality has been covered, but there are corner cases that haven't been cornered as yet.
  5. A comprehensive code review is underway and a number of bugs have been fixed.
  6. Comments, suggestions and bug reports would be most appreciated.

Thursday, October 18, 2012

WCF POX, JSON and SOAP Coexist


Sometimes, we want to make a service available in different protocols so that clients could have an option to choose one of their favorite methods to consume the web services.
Here we are going to talk about how to make one WCF service available in POX(Plain Old XML as XML for short), JSON and SOAP from different endpoints within same service host.
A bit background. A WCF service can have multiple endpoints configured within the same host.
For example, TestService can be exposed at the following endpoints.
  • http://www.example.com//TestService/soap
  • http://www.example.com//TestService/pox
  • http://www.example.com//TestService/json
Each endpoint could have its own binding and endpointBehavior. The binding can specify the protocol that the endpoint agreed with. The endpointBehavior can be utilized to extend the service/client run-time, in this case, it specifies the serialization (more details in later section)
In this section, we will use basicHttpBinding and webHttpBinding that pre-configured by the WCF framework.
  • basicHttpBinding is for services that conform to WS-I Basic Profile 1.1. e.g. SOAP, Envelope, Body etc
  • .
  • webHttpBinding specifies that the service understands generic HTTP requests instead of SOAP requests. The REST service is built on top of generic HTTP request with GET HTTP verb.
Let’s do some coding, first we create a simple service with only one operation contract.
// Service Contract     [ServiceContract(Namespace = "http://www.example.com/service")]     public interface ITestService     {         [OperationContract]         [WebGet]         FooDataContract[] GetFoo(string name, int age, int numberOfFoo);     }
There is WebGet attribute, mocked up on GetFoo operation contract, specifies this operation can
understand HTTP GET verb in the request, the goal is to make the operation RESTful. WebGet attribute is only understood by WCF WebHttpBehavior, so it does not affect the SOAP request since the SOAP endpoint will not use WebHttpBehavior (This will be explained in endpoint configuration section)
You may notice the FooDataContract array is the return type of the method, we will defined a simple class for it.
// DataContract     [DataContract(Name = "Foo", Namespace = "http://www.example.com/data")]     public class FooDataContract     {         [DataMember(Order = 0)]         public string Name { get; set; }         [DataMember(Order = 1)]         public int Age { get; set; }     }
There is nothing much to explain about the datacontract, it simply tells the serializer the name and namespace that the object should be serialized on wire and the ordering of the properties at message level. However the auto property i.e. {get;set;} is a neat feature of .NET 3+
The final step of coding the service is the service implementation.
// ITestService Implementation,     // Each request will initialize its own instance of the TestService     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]     public class TestService : ITestService     {         public FooDataContract[] GetFoo(string name, int age, int numberOfFoo)         {             List<FooDataContract> result = null;             for (int i = 0; i < numberOfFoo; i++)             {                 if (result == null)                     result = new List<FooDataContract>();                 result.Add(new FooDataContract()                 {                     // default to "null"                     Name = (name ?? "null") + "_" + i,                     Age = age                 });             }             // return null or array             return result == null ? null : result.ToArray();         }     }
The service returns a list of FooDataContract objects to the clients according to the parameters.
We have completed the WCF service implementation, but we need to make it configured and hosted for the main purposes of this post, the most interesting part is the configuration part
There are three key areas of configuration i.e. service endpoints, binding and endpointBehavior.
Let’s start with endpoint configuration, it is completed server app.config below.
<configuration>   <system.serviceModel>     <!-- bindings -->     <bindings>       <basicHttpBinding>         <binding name ="soapBinding">           <security mode="None">           </security>         </binding>       </basicHttpBinding>       <webHttpBinding>         <binding name="webBinding">         </binding>       </webHttpBinding>     </bindings>     <!-- behaviors -->     <behaviors>       <endpointBehaviors>         <!-- plain old XML -->         <behavior name="poxBehavior">           <webHttp/>         </behavior>         <!-- JSON -->         <behavior name="jsonBehavior">           <enableWebScript  />         </behavior>       </endpointBehaviors>       <serviceBehaviors>         <behavior name="defaultBehavior">           <serviceDebug includeExceptionDetailInFaults="true" />           <serviceMetadata httpGetEnabled="true" />         </behavior>       </serviceBehaviors>     </behaviors>     <services>       <service name="WcfService.TestService" behaviorConfiguration="defaultBehavior">         <host>           <baseAddresses>             <!-- note, choose an available port-->             <add baseAddress="http://localhost:81/TestService" />           </baseAddresses>         </host>         <endpoint address="soap"                   binding="basicHttpBinding"                   bindingConfiguration="soapBinding"                   contract="WcfService.ITestService" />         <endpoint address="pox"                    binding="webHttpBinding"                   bindingConfiguration="webBinding"                    behaviorConfiguration="poxBehavior"                   contract="WcfService.ITestService" />         <endpoint address="json"                   binding="webHttpBinding"                   bindingConfiguration="webBinding"                    behaviorConfiguration="jsonBehavior"                   contract="WcfService.ITestService" />       </service>     </services>   </system.serviceModel> </configuration>
As mentioned earlier, there will be three endpoints available, pox, json as REST service and soap as SOAP service.
The SOAP endpoint uses basicHttpBinding which comes from WCF, it specifies the endpoint uses SOAP as service (You can specify the version of the message, security etc in “soapBinding” section, but will not be explained here in details).
The POX endpoint uses webHttpBinding with “webBinding” configuration section and the endpointBehavior is poxBehavior.
This tells the POX endpoint it can receive generic HTTP requests and the poxBehavior with webHttp element tells the current endpoint will provide web style services and also at run time, it looks for WebGet attribute on the operation contract in order to determine if the parameters can be parsed from the URI/URL in the GET request. The result is in XML (Plain Old way that you normally see how XML looks like, not SOAP).
The JSON endpoint is specified by webHttpBinding again because it will surely understand generic HTTP request. The special part in this endpoint is the endpoint behavior configuration which is jsonBehavior. It contains enableWebScript element which is derived type of webHttp used in POX endpoint behavior, this means on top of POX REST service, it enables the JavaScript proxy endpoint which allows AJAX calls from Browser. In current context, the purpose is to make the response data to the clients in JSON format.
The code below is the console host for the service.
class Program     {         static void Main(string[] args)         {             // per call instancing             ServiceHost host = new ServiceHost(typeof(TestService));             // start listening             host.Open();             Console.WriteLine("Service started \nPress [Enter] to Close");             Console.ReadLine();             host.Close();         }     }
We have done most of the service, it is time to write a client to test against it. It is a bit tricky to get the client going by calling each of the endpoint.
Firstly, create a new console project and add the service reference to http://localhost:81/TestService
Now you have automatically generated proxy with configuration in the app.config
The generated content within app.config file is useless, we need to manually configure it.
Here is a completed configured client app.config
<?xml version="1.0" encoding="utf-8" ?> <configuration>     <system.serviceModel>       <!-- bindings -->       <bindings>         <basicHttpBinding>           <binding name ="soapBinding">             <security mode="None">             </security>           </binding>         </basicHttpBinding>         <webHttpBinding>           <binding name="webBinding">           </binding>         </webHttpBinding>       </bindings>       <!-- behaviors -->       <behaviors>         <endpointBehaviors>           <!-- plain old XML -->           <behavior name="poxBehavior">             <webHttp/>           </behavior>           <!-- JSON -->           <behavior name="jsonBehavior">             <enableWebScript  />           </behavior>         </endpointBehaviors>         <serviceBehaviors>           <behavior name="defaultBehavior">             <serviceDebug includeExceptionDetailInFaults="true" />             <serviceMetadata httpGetEnabled="true" />           </behavior>         </serviceBehaviors>       </behaviors>         <client>           <endpoint address="http://localhost:81/TestService/soap"                     binding="basicHttpBinding"                     bindingConfiguration="soapBinding"                     contract="TestServiceReference.ITestService" /> <!-- only one endpoint can be configured for client  at a time           <endpoint address="http://localhost:81/TestService/pox"                      binding="webHttpBinding"                     bindingConfiguration="webBinding"                      behaviorConfiguration="poxBehavior"                     contract="TestServiceReference.ITestService" />           <endpoint address="http://localhost:81/TestService/json"                     binding="webHttpBinding"                     bindingConfiguration="webBinding"                      behaviorConfiguration="jsonBehavior"                     contract="TestServiceReference.ITestService" /> -->         </client>     </system.serviceModel> </configuration>
In client Program.cs file, calling the method GetFoo with SOAP,
Remember, only one endpoint can be configured at a time, let’s try the SOAP one first by commenting out the other two endpoints,
<!-- ***http headers*** POST /TestService/soap HTTP/1.1 Content-Type: text/xml; charset=utf-8 SOAPAction: "http://www.example.com/service/ITestService/GetFoo" Host: localhost:81 Content-Length: 211 Expect: 100-continue --> <!-- SOAP --> <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">   <s:Body>     <GetFoo xmlns="http://www.example.com/service">       <name>wall-e</name>       <age>256</age>       <numberOfFoo>2</numberOfFoo>     </GetFoo>   </s:Body> </s:Envelope>
The request is HTTP POST with SOAP content to the service.
It is the corresponding SOAP response below.
Yes, we have successfully made a SOAP request and obtained a SOAP response.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">   <s:Body>     <GetFooResponse xmlns="http://www.example.com/service">       <GetFooResult xmlns:a="http://www.example.com/data" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">         <a:Foo>           <a:Name>wall-e_0</a:Name>           <a:Age>256</a:Age>         </a:Foo>         <a:Foo>           <a:Name> wall-e_1 </a:Name>           <a:Age>256</a:Age>         </a:Foo>       </GetFooResult>     </GetFooResponse>   </s:Body> </s:Envelope>
Let’s try POX endpoint, comment out SOAP and JSON endpoint and leave pox endpoint uncommented. Run the client again. Here we come to a tricky part, we get an exception!
Operation ‘GetFoo’ of contract ‘ITestService’ specifies multiple request body parameters to be serialized without any wrapper elements. At most one body parameter can be serialized without wrapper elements. Either remove the extra body parameters or set the BodyStyle property on the WebGetAttribute/WebInvokeAttribute to Wrapped.
This means by default, the client tries to request the service with POST, therefore client tries to serialize the parameters in the http body. Without SOAP, there is no way to serialize
GetFoo(“wall-e”, 256, 2) into XML
<!–  this is not XML –>
      <name>wall-e</name>
      <age>256</age>
      <numberOfFoo>2</numberOfFoo>
unless you specify the body style should be wrapped automatically.
<!–  this is wrapped XML –>
      <request>
        <name>wall-e</name>
        <age>256</age>
        <numberOfFoo>2</numberOfFoo>
      </request>
However, we want to call the service by GET, not POSTing the data to the server for a “GET” actions. There is an important extra step to do in order to let the client dispatch the HTTP request with parameters in the URL which conforms to the REST service call. It is the WebGet attribute on the client side, while generating the proxy by adding service reference, WebGet attribute does not add to the service contract automatically. WebGet attribute is also required on the client side for consuming the service in the REST way. (Remember, webHttp and enableWebScript behavior look for this attribute to design the HTTP request in the earlier discussion). What we need to do is simply locate the file “reference.cs” generated after adding service reference.
Search keyword: “public interface ITestService” within the client project, you will find the generated service contract, add [System.ServiceModel.Web.WebGet] on top of the OperationContract.
[System.ServiceModel.Web.WebGet]
WcfServiceClient.TestServiceReference.Foo[] GetFoo(string name, int age, int numberOfFoo);
OK, it is done, run the client again,
// REST request with parameters in the URL
GET /TestService/pox/GetFoo?name=wall-e&age=256&numberOfFoo=2 HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: localhost:81
The request no more contains any HTTP body as all the information is expressed in the URL.
The following replied XML (Plain Old XML) is really just the serialized form of array of FooDataContract.
<ArrayOfFoo xmlns="http://www.example.com/data" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">   <Foo>     <Name>wall-e_0</Name>     <Age>256</Age>   </Foo>   <Foo>     <Name>wall-e_1</Name>     <Age>256</Age>   </Foo> </ArrayOfFoo>
The JSON request, this is the final test in this session,
The request is exactly the same as the POX request,
// REST request with parameters in the URL
GET /TestService/json/GetFoo?name=wall-e&age=256&numberOfFoo=2 HTTP/1.1
Content-Type: application/xml; charset=utf-8
Host: localhost:81
As expected the response is in JSON format.
{"d":[       {         "__type":"Foo:http:\/\/www.example.com\/data",         "Name":"wall-e_0",         "Age":256       },       {         "__type":"Foo:http:\/\/www.example.com\/data",         "Name":"wall-e_0",         "Age":256       }     ] }
This example uses windows console as service self-host, in IIS6 or 7, it will be slightly different.
The client utilizes Visual Studio 2008 service proxy generation, in real world, programmers might choose generic WebClient to consume the REST service. A must-have tool during web service development is a http proxy analyzer, not only it can show you how things have been done at the wire level, also it helps a lot to debug the services and clients during the development. All the raw JSON, XML, SOAP or even HTTP headers are exposed to programmers. There are free and commercial tools available, Google will find one of the best HTTP proxy analyzers for you.