NServiceBus for dummies who want to be smarties 4… fourth post, this time on how to incorporate NServiceBus as a backend in an ASP.NET MVC application.
How to create the projects
First, create a new ASP.NET MVC project. I assume you know how to use an IoC container to instantiate controllers, and that you know how to configure your container (otherwise, there are plenty of information available – e.g. here and here), so I will just show this simple snippet on how to build the bus and put it in the container:
In Application_Start:
1 |
ControllerBuilder.Current.SetControllerFactory(new WindsorControllerFactory(CreateWindsorContainer())); |
– and then the container can be created like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
IWindsorContainer CreateWindsorContainer() { var container = new WindsorContainer(); // register controllers and other stuff here Configure.WithWeb() .CastleWindsorBuilder(container) .XmlSerializer() .MsmqTransport() .IsTransactional(true) .PurgeOnStartup(true) .UnicastBus() .CreateBus() .Start(); return container; } |
Notice the call to the extension method CastleWindsorBuilder? It comes from the assembly NServiceBus.ObjectBuilder.CastleWindsor.dll which is included with NServiceBus – it provides the adapters necessary for NServiceBus to a) register everything it discovers (like e.g. all your implementations of IHandleMessages<>), and b) pull instances when needed thus supplying dependencies during object activation. This way of supporting multiple IoC containers just plain ROCKS!
At this point NServiceBus has registered itself and registered all message handlers found in the current web application’s bin directory ( Configure.WithWeb() as opposed to Configure.With()) in the supplied IoC container, and since I am pulling all my controllers from Windsor, I can jump directly to implementing my HomeController:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class HomeController : TxBaseController { readonly IBus bus; public HomeController(IBus bus) { this.bus = bus; } [AcceptVerbs(HttpVerbs.Post)] public RedirectToRouteResult DoSomething(string text) { bus.Send(new SomethingHappened {Message = text}); return RedirectToAction("Index"); } [AcceptVerbs(HttpVerbs.Post)] public RedirectToRouteResult DoSomethingBad(string text) { bus.Send(new SomethingHappened { Message = text }); throw new ApplicationException("oh noes!"); } public ViewResult Index() { return View(); } } |
As you can see, I implemented two actions responding to a HTTP post, both sending a message to the backend, but DoSomethingBad throws an exception, in which case i do not want the message to actually be sent.
My view is just a trivial (Spark) view with two forms:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<use master="Site"/> <h1>DoSomething</h1> <form method="post" action="!{Url.Action("DoSomething")}"> Enter text: <input type="text" name="text" /> <input type="submit" value="Send" /> </form> <h1>DoSomethingBad</h1> <form method="post" action="!{Url.Action("DoSomethingBad")}"> Enter text: <input type="text" name="text" /> <input type="submit" value="Send" /> </form> |
And then I made a simple backend with one message handler:
1 2 3 4 5 6 7 |
public class HandleSomethingHappened : IHandleMessages<SomethingHappened> { public void Handle(SomethingHappened message) { Console.WriteLine("Something happened: {0}", message.Message); } } |
Now, to check if things work, I navigate to /home/index, which on my computer looks like this:
– and then I enter the text “WHEE!” into the first text field and press “Send”, yielding the following in my backend’s console window:
Which is great! My backend receives messages from my web application, so now I can put all kinds of heavy calculations and processesing and whatnot in there, to be computed outside of web requests.
Now, what if something bad happens during the web request, and we do not want any messages to actually be sent? Well, if I enter a text into the other text field and submit it (to the DoSomethingBad action of my HomeController), nothing happens on the backend! Why is that?
Simply because I have started a TransactionScope in my controller layer supertype, TxBaseController, ensuring that all controller actions are run inside a (possibly distributed) transaction! It looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public abstract class TxBaseController : Controller { TransactionScope currentTx; protected override void OnActionExecuting(ActionExecutingContext filterContext) { currentTx = new TransactionScope(); base.OnActionExecuting(filterContext); } protected override void OnActionExecuted(ActionExecutedContext filterContext) { base.OnActionExecuted(filterContext); var exceptionHappened = filterContext.Exception != null; var exceptionWasHandled = filterContext.ExceptionHandled; if (!exceptionHappened || exceptionWasHandled) { currentTx.Complete(); } currentTx.Dispose(); } } |
– and since message queueing plays along nicely with the currently ongoing ambient transaction (which e.g. the incredible NHibernate ALSO does), this transaction scope will make sure that everything just works!
Conclusion
That concludes today’s “NServiceBus for dummies who want to be smarties”. This one was on how to actually put NServiceBus to use as a simple backend for an ASP.NET MVC web application. Freaking easy, right?! Next time I will show how to put sagas to use by implementing one of the workflows of a typical web application.