Tuesday, November 24, 2009

Calling WCF service from jquery

Over the weekend I was working on a piece of code wherein I was supposed to call a WCF service from jquery (i.e. exposing WCF as REST based service).
I found it really interesting and thought of sharing the details (unfortunately, I could not find a way to attach the sample solution to blogspot).
In this example, I call up a WCF service from jquery and show the result (Products) on a page with pagination support.  To format the result, I use jtemplates.

Here are the steps:
1. First, created an asp.net ajax project and, like all WCF service, create an interface which will be the service contract. Let call this IProductService.


    [ServiceContract(Namespace = "urn:SampleApps/Services")]
    interface IProductService
    {
        [OperationContract]
        [WebGet]
        ProductResult GetProducts(int StartIndex, int PageSize);
    }


The important things to note out here are the namespace in ServiceContract attribute and the [WebGet] attribute.
The first one is used to group our service contracts into namespaces like we do for a C# file and the second one exposes allows it to receive GET messages (which is required for REST calls)


2. Implement this service contract as we do a normal service contract.


    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class ProductService : IProductService
    {
        #region IProductService Members
        public ProductResult GetProducts(int StartIndex, int PageSize)
        {
            return ProductFactory.GetProducts(StartIndex, PageSize);
        }

        #endregion
    }


In the above code ProductFactory.GetProducts returns us the products to be displayed on a page. We can have any logic out here to return the products.

3. Create an ASPX page and add references to jquery.js file and optionally to jquery-jtemplates.js


    <script type="text/javascript" src="/JS/jquery-1.3.2.js"></script>
    <script type="text/javascript" src="/JS/jquery-jtemplates.js"></script>


4. Add a script manager tag and refer the service as below on the aspx page

    <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Services>
            <asp:ServiceReference Path="~/ProductService.svc" />
        </Services>
    </asp:ScriptManager>


5. Now the interesting stuff, the below JavaScript code calls up the service and gets the result

    <script type="text/javascript">
        var PageCount = 0;

        function GetProducts(StartIndex, PageSize) {
            var proxy = new SampleApps.Services.IProductService();
            proxy.GetProducts(StartIndex, PageSize, ProductsReturnedEventHandler, ProductsFailedEventHandler, null);
        }

        function ProductsReturnedEventHandler(value) {
            PageCount = Math.ceil(value.TotalRecords / value.PageSize);
            $('#divSearchResults').setTemplate($('#templateContainer').html());
            $('#divSearchResults').processTemplate(value);

            var paging = "<table align='center'><tr>";
            for (var pgN = 1; pgN <= PageCount; pgN++) {
                paging += "<td><a href='#' onClick='ChangePage(this)' id='pagingHref_" + pgN + "'>" + pgN + "</td>";
            }
            paging += "</tr></table>";
            $('#divPaging').html(paging);                 
        }

        function ChangePage(obj) {
            var pgN = $(obj).text();
            var pageSize = 10;
            var startIndex = (pgN - 1)* pageSize;
            GetProducts(startIndex, pageSize);
        }
        
        function ProductsFailedEventHandler(value) {
        }
    </script>

One good feature is that we get full intelligence support in JavaScript for service call as soon as we register the service in script manger :)

6. On the page we have a template section where the results are binded.

    <div> 
    <input type="button" id="btnClick" name="btnClick" value="Click Here" onclick="GetProducts(0, 10);" />
   <div id="divSearchResults" style="font-family: Verdana; font-size: xx-small;">
    </div>    
    <div id="templateContainer" style="display:none;">
       <table style="font-family: Verdana; font-size: xx-small; width:60%">
            <thead style="background-color:Blue; color:White; font-size:larger; font-weight:bold">
                <tr>
                    <td>ID</td>
                    <td>Name</td>
                    <td>Price</td>
                </tr>
            </thead>
            {#foreach $T.Products as post}
            <tr>      
                <td>
                    {$T.post.ProductID}
                </td>
                <td>
                    {$T.post.ProductName}
                </td>
                <td>
                    {$T.post.ProductPrice}
                </td>
            </tr>
            <tr>
                <td colspan="3"><hr /></td>
            </tr>
            {#/for}
        </table>    
    </div>
    <div id="divPaging" />    
    </div>



7. Images:

a. Solution Structure


b. Final result