Wednesday, May 12, 2010 7:29 PM Kazi Manzur Rashid

MvcExtensions – Bootstrapping

When you create a new ASP.NET MVC application you will find that the global.asax contains the following lines:

namespace MvcApplication1
{
    // 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 void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

        }

        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            RegisterRoutes(RouteTable.Routes);
        }
    }
}

As the application grows, there are quite a lot of plumbing code gets into the global.asax which quickly becomes a design smell. Lets take a quick look at the code of one of the open source project that I recently visited:

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute("Default","{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "" });
        }        
        protected override void OnApplicationStarted()
        {
            Error += OnError;
            EndRequest += OnEndRequest;
            
             var settings = new SparkSettings()
                .AddNamespace("System")
                .AddNamespace("System.Collections.Generic")
                .AddNamespace("System.Web.Mvc")
                .AddNamespace("System.Web.Mvc.Html")
                .AddNamespace("MvcContrib.FluentHtml")
                .AddNamespace("********")
                .AddNamespace("********.Web")
                .SetPageBaseType("ApplicationViewPage")
                .SetAutomaticEncoding(true);
                               
#if DEBUG
            settings.SetDebug(true);
#endif
            var viewFactory = new SparkViewFactory(settings);
            ViewEngines.Engines.Add(viewFactory);
#if !DEBUG            
            PrecompileViews(viewFactory);
#endif            

            RegisterAllControllersIn("********.Web");

            log4net.Config.XmlConfigurator.Configure();            
            RegisterRoutes(RouteTable.Routes);

            Factory.Load(new Components.WebDependencies());
            ModelBinders.Binders.DefaultBinder = new Binders.GenericBinderResolver(Factory.TryGet<IModelBinder>);

            ValidatorConfiguration.Initialize("********");
            HtmlValidationExtensions.Initialize(ValidatorConfiguration.Rules);
        }

        private void OnEndRequest(object sender, System.EventArgs e)
        {
            if (((HttpApplication)sender).Context.Handler is MvcHandler)
            {
                CreateKernel().Get<ISessionSource>().Close();
            }
        }
        private void OnError(object sender, System.EventArgs e)
        {
            CreateKernel().Get<ISessionSource>().Close();
        }
        protected override IKernel CreateKernel()
        {
            return Factory.Kernel;
        }
        
        private static void PrecompileViews(SparkViewFactory viewFactory)
        {
            var batch = new SparkBatchDescriptor();
            batch.For<HomeController>().For<ManageController>();
            viewFactory.Precompile(batch);                  
        }

As you can see there are quite a few of things going on in the above code, Registering the ViewEngine, Compiling the Views, Registering the Routes/Controllers/Model Binders, Settings up Logger, Validations and as you can imagine the more it becomes complex the more things will get added in the application start.

One of the goal of the MVCExtensions is to reduce the above design smell. Instead of writing all the plumbing code in the application start, it contains BootstrapperTask to register individual services. Out of the box, it contains BootstrapperTask to register Controllers, Controller Factory, Action Invoker, Action Filters, Model Binders, Model Metadata/Validation Providers, ValueProvideraFactory, ViewEngines etc and it is intelligent enough to automatically detect the above types and register into the ASP.NET MVC Framework. Other than the built-in tasks you can create your own custom task which will be automatically executed when the application starts. When the BootstrapperTasks are in action you will find the global.asax pretty much clean like the following:

public class MvcApplication : UnityMvcApplication
{
    public void ErrorLog_Filtering(object sender, ExceptionFilterEventArgs e)
    {
        Check.Argument.IsNotNull(e, "e");

        HttpException exception = e.Exception.GetBaseException() as HttpException;

        if ((exception != null) && (exception.GetHttpCode() == (int)HttpStatusCode.NotFound))
        {
            e.Dismiss();
        }
    }
}

The above code is taken from my another open source project Shrinkr, as you can see the global.asax is longer cluttered with any plumbing code. One special thing you have noticed that it is inherited from the UnityMvcApplication rather than regular HttpApplication. There are separate version of this class for each IoC Container like NinjectMvcApplication, StructureMapMvcApplication etc.

Other than executing the built-in tasks, the Shrinkr also has few custom tasks which gets executed when the application starts. For example, when the application starts, we want to ensure that the default users (which is specified in the web.config) are created. The following is the custom task that is used to create those default users:

public class CreateDefaultUsers : BootstrapperTask
{
    protected override TaskContinuation ExecuteCore(IServiceLocator serviceLocator)
    {
        IUserRepository userRepository = serviceLocator.GetInstance<IUserRepository>();
        IUnitOfWork unitOfWork = serviceLocator.GetInstance<IUnitOfWork>();
        IEnumerable<User> users = serviceLocator.GetInstance<Settings>().DefaultUsers;

        bool shouldCommit = false;

        foreach (User user in users)
        {
            if (userRepository.GetByName(user.Name) == null)
            {
                user.AllowApiAccess(ApiSetting.InfiniteLimit);

                userRepository.Add(user);
                shouldCommit = true;
            }
        }

        if (shouldCommit)
        {
            unitOfWork.Commit();
        }

        return TaskContinuation.Continue;
    }
}

There are several other Tasks in the Shrinkr that we are also using which you will find in that project. To create a custom bootstrapping task you have create a new class which either implements the IBootstrapperTask interface or inherits from the abstract BootstrapperTask class, I would recommend to start with the BootstrapperTask as it already has the required code that you have to write in case if you choose the IBootstrapperTask interface. As you can see in the above code we are overriding the ExecuteCore to create the default users, the MVCExtensions is responsible for populating the  ServiceLocator prior calling this method and in this method we are using the service locator to get the dependencies that are required to create the users (I will cover the custom dependencies registration in the next post). Once the users are created, we are returning a special enum, TaskContinuation as the return value, the TaskContinuation can have three values Continue (default), Skip and Break. The reason behind of having this enum is, in some  special cases you might want to skip the next task in the chain or break the complete chain depending upon the currently running task, in those cases you will use the other two values instead of the Continue. The last thing I want to cover in the bootstrapping task is the Order. By default all the built-in tasks as well as newly created task order is set to the DefaultOrder(a static property), in some special cases you might want to execute it before/after all the other tasks, in those cases you will assign the Order in the Task constructor. For Example, in Shrinkr, we want to run few background services when the all the tasks are executed, so we assigned the order as DefaultOrder + 1. Here is the code of that Task:

public class ConfigureBackgroundServices : BootstrapperTask
{
    private IEnumerable<IBackgroundService> backgroundServices;

    public ConfigureBackgroundServices()
    {
        Order = DefaultOrder + 1;
    }

    protected override TaskContinuation ExecuteCore(IServiceLocator serviceLocator)
    {
        backgroundServices = serviceLocator.GetAllInstances<IBackgroundService>().ToList();

        backgroundServices.Each(service => service.Start());

        return TaskContinuation.Continue;
    }

    protected override void DisposeCore()
    {
        backgroundServices.Each(service => service.Stop());
    }
}

That’s it for today, in the next post I will cover the custom service registration, so stay tuned.

Shout it
Filed under: , , , , , ,

Comments

# re: MvcExtensions – Bootstrapping

Wednesday, May 12, 2010 8:57 AM by Nicolas

I think the 3rd code excerpt it not the correct one...

# Twitter Trackbacks for MvcExtensions ??? Bootstrapping - Kazi Manzur Rashid's Blog [asp.net] on Topsy.com

Pingback from  Twitter Trackbacks for                 MvcExtensions ??? Bootstrapping - Kazi Manzur Rashid's Blog         [asp.net]        on Topsy.com

# re: MvcExtensions – Bootstrapping

Wednesday, May 12, 2010 9:18 AM by Kazi Manzur Rashid

@Nicolas : Could you pls tell what is not correct?

# MvcExtensions – Bootstrapping

Wednesday, May 12, 2010 9:40 AM by DotNetShoutout

Thank you for submitting this cool story - Trackback from DotNetShoutout

# MvcExtensions ??? Bootstrapping | OOP - Object Oriented Programing

Pingback from  MvcExtensions ??? Bootstrapping | OOP - Object Oriented Programing

# re: MvcExtensions – Bootstrapping

Wednesday, May 12, 2010 10:14 AM by daniel.nutu

the mvc extensions are really great, but after a couple of issues i had with unity i switched to autofac. from what i've seen it's faster than other IoC containers and it's development is ongoing (unlike unity which is updated every 2 years or so).

i'd really like to see a bootstrapper for autofac which i think will be trickier.

# re: MvcExtensions – Bootstrapping

Wednesday, May 12, 2010 10:40 AM by Kazi Manzur Rashid

@Daniel: Autofac is in my feature list, in fact when I started it, it was included but later on I have to drop it due to the lack of support for custom lifetime management. Though I have not checked the recent version, will do soon.

Sorry I could not agree with on the last part, I myself did a benchmark with all the containers which also includes the MEF and StructureMap was fastest one. But I think the Containers performance is not at all a concern when you want enrich your overall application performance.

# MvcExtensions – Bootstrapping

Wednesday, May 12, 2010 11:25 AM by progg.ru

Thank you for submitting this cool story - Trackback from progg.ru

# MvcExtensions – Bootstrapping http://weblogs.asp.net/rashid/archive/2010/05/12/mvcextensions-bootstrapping.aspx #mvc

Thursday, May 13, 2010 3:04 AM by Twitter Mirror

MvcExtensions – Bootstrapping http://weblogs. asp.net /rashid/archive/2010/05/12/mvcextensions-bootstrapping

# IT???????????? &raquo; Blog Archive &raquo; ASP.NET MVC?????????

Pingback from  IT????????????  &raquo; Blog Archive   &raquo; ASP.NET MVC?????????

# MvcExtensions – Custom Service Registration

Tuesday, May 18, 2010 7:47 AM by Kazi Manzur Rashid's Blog

[Special Note: I have just released SP1 Preview which contains the newly added Autofac Adapter and minor

# MvcExtensions ??? Custom Service Registration | OOP - Object Oriented Programing

Pingback from  MvcExtensions ??? Custom Service Registration | OOP - Object Oriented Programing

# ASP.NET MVC Archived Blog Posts, Page 1

Wednesday, May 19, 2010 12:36 AM by ASP.NET MVC Archived Blog Posts, Page 1

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

# MvcExtensions - PerRequestTask

Wednesday, May 19, 2010 9:48 AM by Kazi Manzur Rashid's Blog

In the previous post , we have seen the BootstrapperTask which executes when the application starts and

# MvcExtensions &#8211; PerRequestTask | OOP - Object Oriented Programing

Pingback from  MvcExtensions &#8211; PerRequestTask | OOP - Object Oriented Programing