Thursday, February 28, 2013

Using MVC style controllers in ASP.NET WebForms with AJAX

I have been working on a project that is based in ASP.NET web forms recently with the knowledge that a near-future generation of this software will be created using MVC.  Always trying to find a way to share code and create leverage with solid SOA design, I decided to drive the ASP.NET application from controllers interacting with services.  By doing this, I am am assuring that not only the service layers are portable to other platforms, but that the controllers can be reused as well.  This gives us another layer of code reuse and a mechanism to limit the testing requirements on the future application.  The first step is to integrate the MVC references into your ASP application.  In order to do this, you must add the references to your project and include them in your web.config as follows.


Next, we have to make sure that our routes are created using the MVC 'magic'.  This is accomplished with a global.asax file executing the .net map paths functionality.  This is done via a single method call at the start of the application as illustrated below.
Code:
public class Global : HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapRoute(
            "Default",
            "{controller}/{action}/{id}",
            new {id = UrlParameter.Optional}
         );
    }
}

At this point, we are able to add controllers to interact with our data. The controller classes must be inherited from the Controller object. Once this class is stubbed in, you are able to simply frame in your methods to interact with the data as needed. Here is an example of a controller class.
Code:
public class TaskController : Controller    
{

    /// <summary>
    /// Completes the task.
    /// </summary>
    /// <param name="taskId">The task id.</param>
    /// <returns></returns>
    public ActionResult CompleteTask(int taskId)
    {
      
        try
        {
            MyTask data = ***service call to complete task and return updated data
            return Json(data, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            return Json("");
        }
    }

    /// <summary>
    /// Get a list of tasks.
    /// </summary>
    /// <returns></returns>
    public ActionResult TaskList()
    {
       
        try
        {   
            List<MyTask> tasks = *** Service call to get task list.

            return Json(tasks, JsonRequestBehavior.AllowGet);
        }
        catch (Exception ex)
        {
            return Json("");
        }
    }
}

The last step is to simply consume these controllers via AJAX calls in your client.
Code:
function loadTasks() {
    
    $.mobile.showPageLoadingMsg();
    var request = $.ajax({
        url: "/Task/TaskList",
        type: "POST",
        data: {},
        context: this
    });

    request.done(downloadDone).fail(downloadFailed).error(downloadError);
}

function downloadDone(responseData) {

    $.each(responseData, function (i, obj) {
         // do something with each task from the list by using obj.* to reference values.

    $.mobile.hidePageLoadingMsg();

}
function downloadFailed() {
    alert("Could not retrieve task data at this time.");
    $.mobile.hidePageLoadingMsg();
}

function downloadError(error) {
    $.mobile.hidePageLoadingMsg();
    console.log(error);
}

At this point, I can add controllers that represent my service interaction layer, and then simply reuse them when the application is migrated to MVC. This gives us the ability to separate the actual presentation layer from its interaction with the services and target that logic for reuse.