The Costa Rica pattern

Named after the Central American country where I developed it. My web apps are mostly basic CRUD business apps, pulling data from the database, let the use edit it and save it back.
My main application was developed in 2005 with .net 1.1 so it used asp.net web forms. Over the years I added some Ajax features using the asp.net Ajax framework and toolkit. Like web forms it abstracted a lot of the details away from the developer. In case of update panels it could make pages load slower if used in inappropriate cases.
With the common trend of "going back to the roots" to have full control over your html I am now backing away from web forms, but rather than going to asp.net MVC I am going straight to full-Ajax.
I only use server side code to display the initial page and then use Ajax calls to get more data or post data back to the server. There are no more traditional asp.net postbacks.
The JavaScript Library
After using Ajax for several years in my business applications I can identify six main use cases: I used to use various different methods to make the appropriate Ajax calls, using pure JavaScript, the Asp.net Ajax library and toolkit and jQuery. In general these use cases result in the following ways to make calls:
  1. Taking parameter(s) in the querystring or a form field collection, process that input on the server and return html, text or json to be displayed on the page.
  2. Taking parameters as Json, process them and return a json formatted result. Mainly used to return complete objects to the client.
  3. Taking form data to be saved or processed on the server and return a structured result in XML that contains some sort of return status code and possibly validation or other errors.
My new favourite JavaScript library jQuery offers several ways to fire an Ajax request, load(), get(), post() and Ajax(). I introduced an abstracation layer on top of all these, it contains two objects, 'Call' and 'Result'. A basic call could be:
function GetItems()
{
  var cal = new Topas.Service.Call("GetItems");
  cal.AddParameter("groupID",15);
  cal.ResultFormat = "html";
  cal.Submit();
}
This will call a page named 'service.aspx' in the same directory with two form parameters, groupid=15 and tn_op=GetItems and return a list of items as html.
function GetItem()
{
  var cal = new Topas.Service.Call("./GetItem");
  cal.AddParameter("id",358);
  cal.Submit();
}
This submits to service.aspx/GetItem with json {"id:358"} as form parameter. It returns json formatted data.
function SaveItem()
{
  var cal = new Topas.Service.Call("SaveItem");
  cal.DataContainerId = 'form1';
  cal.Submit();
}
This calls service.aspx with all form field values within form1 plus tn_op = SaveItem and returns a standard xml document. It excludes asp.net internal hidden fields and submits the form fields with the original names without the junk added by asp.net at the beginning.

So all three different ways to make Ajax calls are done throught the same object. It has some conventional shortcuts. A simple string as the constructor parameter means call 'service.aspx' in the same directory as the page. The string is the operation name used on the server to determine which method to execute. The return format is xml or json depending whether we call a normal method or a web method unless overwritten using the ResultFormat property.
Adding parameters is always the same regardless of whether it is a form collection submission or json.

If the operator string in the constructor has a slash, the parameters are formatted as json and the response is expected to be json as well, this works very well with asp.net WebMethods.
Just as we are using a single object for all requests, there is only one for all possible responses. It is the 'Result' object. In the callback function, you create the object passing in the response data coming back from the server.
The callback method name is also based on the operation in the constructor, 'Completed' is appended to the operation and used as the name of the callback function.
function GetItemsCompleted(serverData)
{
  $("#pnlItemList").html(serverdata);
}
This is the simplest call function, we know GetItems returns pure html, so all we do it put that html into the innerHtml of our result div.
function GetItemCompleted(serverData)
{
    var res = new Topas.Service.Result(serverData);

    if (res.Okay)
    {
        $('#txtName').val(res.Data.Name);
        $('#txtNumber').val(res.Data.Number);
    }
    else
    {
        Topas.ShowInfo(res.Text, true);
    }
}
In this case our call returns a JSON formatted object, here we introduce our result object, its constructor takes the raw data from the server and processes it. First we can ask whether the call method was successful (res.Okay) and then we can access the properties of the object via their names and put them into html input fields or whatever we want to do with them.
A helper method ShowInfo displays a message to the user, the true parameter indicates that this should be a warning.
function SaveItemCompleted(serverData)
{
    Topas.ClearValidationHighlighting();

    var res = new Topas.Service.Result(serverData);
    
    if (res.Updated)
    {
        Topas.ShowInfo("Item saved");
    }
    else if (res.Inserted)
    {
        Topas.ShowInfo("Item added with Id: " + res.Id);
    }
    else if (res.Invalid)
    {
        res.ShowValidationErrors();
        Topas.ShowInfo("Validation errors found", true);
    }
    else
    {
        Topas.ShowInfo("Problem, status: " + res.Text, true);
    }    
}
Out 'Save' methods usually return XML rather than JSON, but we don't care because the result object parses both JSON and XML data into the same properties.

First we call the helper method 'ClearValidationHighlighting' which as the name suggest clears any previous highlighting of validation errors on the page.
Then we create our result object, again passing in the raw data from the server. We then check for the status of the operation. If an item was updated, we simply display a message, for new items we display a different message. The result object has an Id field which may be filled on the server.

If the server returns an 'invalid' status, this means that validation errors were found. All validation is taken place within the business layer on the server and a standard XML document is returned when errors are found. The helper method 'ShowValidationErrors' knows the format of this document and can automatically highligh the affected fields and display messages for them.
For any other status (usually errors) we display a message.

The Error and time-out call back functions are common functions and simply display a message using Topas.ShowInfo. These methods can be overwritten, but using the defaults keeps the code clean.
On the server
On the server I am still using traditional business objects to hold my data (POCOs), however if they want to participate in the CostaRica pattern they have to support the IBizObject interface. This interface only has two properties:
System.Net.HttpStatusCode BizStatus { get; set; }
string BizMessage { get; set; }
The objects are passed to the client JSON serialized and the client side framework checks for these two properties to determine the status of the object. If I want to pass a simple message to the client, I don't just use a string anymore, but to use a BizResult object, which implements IBizObject and only adds two properties 'Id' and 'Content' .
[WebMethod]
public static SomeLib.BizResult DoStuff()
{
    SomeLib.BizResult result = new SomeLib.BizResult();
    // do stuff...
    result.Id = newId; 
    return result;
}
We create our result object, by default is status is OK, so unless there is a problem we don't have to change that.
public static SomeLib.BizResult SaveItem()
{
    SomeLib.BizResult result = new SomeLib.BizResult();

    // get the ID
    int id = SomeLib.UserData.GetInteger("hdItemId");

    try
    {
        BizLayer.ItemValidation val = new BizLayer.ItemValidation(id);
        val.SetDataFromWebRequest();
        result = val.RunTestsAndSave();
    }
    catch (Exception ex)
    {
        SomeLib.Exceptions.Log(ex);
        result.SetError(id, ex.Message);
    }

    return result;
}
Now, lets save our item, we create an instance of the validation object that we have for each business object. We can set all the data from the browser with a single call 'SetDataFromWebRequest'. The 'RunTestsAndSave' methods takes that data, runs the defined tests on it and if valid, saves the data. It returns a BizObject which is either set to Ok and to Invalid in which case the validation problems are also part of the object.
In case there was a problem, we log it and fill the result object with data about it.
More advanved stuff
What if we don't want to build html on the server, we can instead push down a .net DataTable as Json
public static string LoadItems()
{
    DataTable tab = object.GetsDataTable()

    return SomeLib.Data.TableHelper.ToJSON(tab, "items");
}
This can't be a WebMethod, as the .net Json serializer does not support DataTables, instead we serialize it ourself.
On the client:
function LoadEntriesCompleted(serverData)
{
    if (returnData.items.length != 0)
    {
        var data = serverData.items;
        var html = "<div>";
        for (var i = 0; i < returnData.entries.length; ++i)
        {
          html += "<div class=\"item\">" + data[i].Name;
          html += data[i].Number + "</div>";
        }
        html += "</div>";
    
        $("#pnlItemList").html(html);
    }
    else
    {
        Topas.ShowInfo("No items found");
    }
}
Again we can call the properties by name. If this is still to ugly, we could use some template html, that we then fill and clone using jQuery.

Pages in this section