BLOG

Asynchronous MVC Controllers + NServiceBus

07 May 2009 by Stuart Cam

NServiceBus is a powerful, yet lightweight, open source messaging framework for designing distributed .NET enterprise systems - just ask Udi Dahan the project founder. NServiceBus gives programmers a head-start on developing robust, scalable, and maintainable service-layers and long-running business processes.

I have personally used NServiceBus on projects and I really like what Udi has put together.

The ASP.NET MVC v1.0 Futures download adds functionality to MVC to enable asynchronous controller actions. The plumbing has already been blogged about by Maarten Balliauw, so I won't repeat it. Maarten has written some sample code to demonstrate the various patterns available (Maarten, your IndexDelegate controller action needs some work!).

I thought it would be interesting to use the MVC futures library in conjuntion with NServiceBus.

The NServiceBus Full Source Download contains an Async Web Forms sample which includes a Server + Web Site. I have simply created an MVC site to interact with the Server.

The main changes to Maarten's code have been in the HomeController.cs file:

using System;
using System.Web.Mvc;
using Microsoft.Web.Mvc;
using System.Threading;
using Messages;
using NServiceBus;

namespace MvcAsyncControllersExample.Controllers
{
    [HandleError]
    public class HomeController : AsyncController
    {
        public ActionResult Index()
        {
            return View();
        }

        private int GetCommandId()
        {
            int value;
            var success = Int32.TryParse(Request.Form["Value"], out value);
            return success ? value : 0;
        }

        // IAsync Method
        public IAsyncResult BeginIndexIAsync(AsyncCallback callback, object state)
        {
            var command = new Command { Id = GetCommandId() };
            return MvcApplication.Bus.Send(command).Register(callback, state);
        }

        public ActionResult EndIndexIAsync(IAsyncResult ar)
        {
            var result = ar.AsyncState as CompletionResult;
            ViewData["Result"] = (result == null) ? "null" : Enum.GetName(typeof(ErrorCodes), result.ErrorCode);
            ViewData["Date"] = DateTime.Now.ToString();
            return View("Index");
        }

        // Event Method (This techniques is designed to 'wrap' a synchronous call with
        //               some plumbing, whereas NServiceBus is asynchronous by design,
        //               so in this instance it seems cumbersome)
        public void IndexEvent()
        {
            AsyncManager.OutstandingOperations.Increment();
            ThreadPool.QueueUserWorkItem(o =>
            {
                var result = new CompletionResult();
                var command = new Command { Id = GetCommandId() };

                var sync = MvcApplication.Bus.Send(command).Register(asyncResult =>
                                                                     {
                                                                         result = asyncResult.AsyncState as CompletionResult;
                                                                     },
                                                                     null);
                sync.AsyncWaitHandle.WaitOne();

                var resultString = (result == null) ? "null" : Enum.GetName(typeof(ErrorCodes), result.ErrorCode);
                AsyncManager.Parameters.Add("Result", resultString);
                AsyncManager.OutstandingOperations.Decrement();

            }, null);
        }

        public ActionResult IndexEventCompleted()
        {
            ViewData["Result"] = AsyncManager.Parameters["Result"];
            ViewData["Date"] = DateTime.Now.ToString();
            return View("Index");
        }

        // Delegate Method
        public Func<ActionResult> IndexDelegate()
        {
            AsyncManager.RegisterTask(callback =>
                                      {
                                          var command = new Command { Id = GetCommandId() };
                                          return MvcApplication.Bus.Send(command).Register(callback, null);
                                      },
                                      ar =>
                                      {
                                          var result = ar.AsyncState as CompletionResult;
                                          ViewData["Result"] = (result == null) ? "null" : Enum.GetName(typeof(ErrorCodes), result.ErrorCode);
                                          ViewData["Date"] = DateTime.Now.ToString();
                                      });
            return () => View("Index");
        }
    }
}

Along with some additional plumbing in the Global.asax.cs:

using System.Web.Mvc;
using System.Web.Routing;
using Microsoft.Web.Mvc;
using NServiceBus;

namespace MvcAsyncControllersExample
{
    // Note: For instructions on enabling IIS6 or IIS7 classic mode, 
    // visit http://go.microsoft.com/?LinkId=9394801

    public class MvcApplication : System.Web.HttpApplication
    {
        public static IBus Bus { get; private set; }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapAsyncRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );
        }

        protected void Application_Start()
        {
            RegisterRoutes(RouteTable.Routes);

            Bus = Configure.WithWeb()
                           .SpringBuilder()
                           .XmlSerializer()
                           .MsmqTransport()
                               .IsTransactional(false)
                               .PurgeOnStartup(false)
                           .UnicastBus()
                               .ImpersonateSender(false)
                           .CreateBus()
                           .Start();
        }
    }
}

MVC web site in action

Asynchronous MVC Controller & NServiceBus sample site

Download the sample code here: MvcAsyncControllersExample_NServiceBus.zip (676.51 kb)

Tags: , , ,

Categories: C Sharp | MVC | SOA


© Codebrain 2017. All Rights Reserved. Registered in England: 07744920. VAT: GB 119 4078 13