This post is over 6 months old. Some details, especially technical, may have changed.

UpdatePanels & Alternatives

I'm not sold on ASP.NET UpdatePanels. Conceptually they are quite nice but their implementation is nasty. So much so I'd be happy to say the should be considered an anti-pattern. My reasons? Well...

  • They generate too much JavaScript
  • It's too "development focused" - it puts saving time ahead of a clean performant solution
  • Ajax calls should be data focused (returning XML, JSON etc), UpdatePanels are content focused
  • Generates a massive amount of network traffic in comparison to alternative solutions (including ViewState and markup)
  • UpdatePanels use innerHTML to replace contents which orphans bound events (leakage) that need to be rebound each time (extra work).

But what are the alternatives? Page Methods and Scripted Services are a good starting point. The good things about these alternatives is that they are lightweight - they send JSON data to and from the server so your network traffic is greatly reduced. Here is a simple example of using a page method - I'll not cover Scripted Services as they are very similar and you can find info about them on the net.

In your code behind you can add a [WebMethod] attributed method

using System;
using System.Web.Services;

namespace WebformControls{
    public partial class PageMethods : System.Web.UI.Page{
        [WebMethod]
        public static string GetCurrentDateTime(){
            return DateTime.Now.ToString();
        }
    }
}

And turn on EnablePageMethods attribute of the pages ScriptManager

<html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
            <asp:ScriptManager runat="server" EnablePageMethods="true"></asp:ScriptManager>
        </form>
    </body>
</html>

What happens next is that when the page is rendered a JavaScript Proxy Class will be generated called PageMethods that contains a function call GetCurrentDateAndTime. It'll look something like this.

var PageMethods = function() {
    PageMethods.initializeBase(this);
    this._timeout = 0;
    this._userContext = null;
    this._succeeded = null;
    this._failed = null;
}
PageMethods.prototype = {
    _get_path: function() {
        var p = this.get_path();
        if (p) return p;
        else return PageMethods._staticInstance.get_path();
    },
    GetCurrentDateTime: function(succeededCallback, failedCallback, userContext) {
        /// 
        /// 
        /// 
        return this._invoke(this._get_path(), 'GetCurrentDateTime', false, {}, succeededCallback, failedCallback, userContext);
    }
}
PageMethods.set_path("/PageMethods.aspx");
PageMethods.GetCurrentDateTime = function(onSuccess, onFailed, userContext) {
    /// 
    /// 
    /// 
    PageMethods._staticInstance.GetCurrentDateTime(onSuccess, onFailed, userContext);
}

Now all we have to do is use it. This is easy...

<html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title></title>
        <script type="text/javascript">
            function getDate() {
                PageMethods.GetCurrentDateTime(function(data) {
                    document.getElementById("results").innerHTML = data;
                })
            }
        </script>
    </head>
    <body>
        <form id="form1" runat="server">
            <asp:ScriptManager runat="server" EnablePageMethods="true"></asp:ScriptManager>
            <a href="#" onclick="getDate()">Get Date!</a>
            <div id="results"></div>
        </form>
    </body>
</html>

That wasn't too difficult now was it? But we aren't done yet. Sure this solution works but there is still one problem - We are including the ScriptManager in our page which means event though all we are doing is getting a date back from the server we are still required to pull down at least 160k (in it's most compressed & gzipped form) of framework 90% of which we aren't even going to use.

Calling PageMethods and ScriptedServices using jQuery

Obviously a request from client to server is always done through HTTP it doesn't actually matter how it is handled on the client. So it's easy to swap out the ASP.NET Ajax framework for jQuery. You'll probably be asking why? Well the long and short of it is that regardless of what the requirements say you (or someone else on the project) WILL end up writing Javascript and jQuery is the better choice...

  • It's a better, smaller, compact and faster framework
  • It's much better documented and easier to learn
  • It's extensible via plugins so you don't need the whole "kitchen sink" to do simple things
  • It can do everything ASP.NET Ajax can do
  • It's technology agnostic so you can jump to a Java project and use the same solutions

I could go on, honestly I could, but that's not what this post is about. There are a few nuances in calling PageMethods and ScriptedServices, namely

  • The request content-type must be application/json; charset=utf-8
  • The request must be a POST request
  • Data sent to the server must be encoded as a JSON string or "{}" if empty
  • Standard ASP.NET JSON serialization doesn't convert DateTime to a proper JSON date
  • The JSON data in the request must map directly to arguments of the function

Failure to comply with any of the above will result in either a security error or, usually, the entire page being rendered back to the response. So calling the above PageMethod using jQuery is as simple as this....

$.ajax({
  type: "POST",
  url: "PageMethods.aspx/GetCurrentDateTime",
  data: "{}",
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function(msg) {
    $('#result').html(msg)
  }
});

Not bad, but there is a lot of boiler plate code there. Thankfully it's easy to write a jQuery plugin to abstract out most of this and make the calls simple. So all in all even with the 2 plugins (JSON and .Net Services) and the jQuery framework we are only forced to pull down 60k of scripts (minified but NOT gzipped) so you could see this reduced to 25-30k all in with gzipping turned on.

Published in .NET on April 14, 2009