Creating Project from Scratch

Creating Project from Scratch

I'm starting a new Asp.Net MVC based site using Spark and Windsor. I thought it would be useful to capture the activity in the form of a walk-through or recipe.

In the interest of generic I'm going to call the site Alex, and the name of the company Company.

After creating an empty solution named Alex close Visual Studio and rearrange the file system. For personal preference the solution is laid out with the following initial import.

Adding references and configuring

After importing this into source control and checking out the trunk directory, reopen Alex.sln and add a new ASP.NET Web Application project named Company.Alex.WebHost. Then Add References..., select Browse tab, and navigating to the ..\..\lib path add the following:

In the web.config add the following

<configuration>
  <configSections>
    <!-- add section handler for spark -->
    <section name="spark" type="Spark.Configuration.SparkSectionHandler, Spark"/>
  </configSections>
 
  <!-- add spark section -->
  <spark>
    <compilation debug="true"/>
  </spark>
 
  <system.web>
    <!-- set debug true -->
    <compilation debug="true">
      ...
    </compilation>
    <httpModules>
      <!-- add routing module -->
      <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </httpModules>
  </system.web>
</configuration>

Initializing environment and routes

Get rid of the normal Default.aspx and associated files, and replace with a single placeholder.

Default.aspx

<!-- placeholder. please do not delete -->

To initialize the web host's environment add a Global.asax HttpApplication and an Application.cs class.

Global.asax.cs

public class Global : HttpApplication
{
    static readonly Application _application = new Application();
 
    protected void Application_Start(object sender, EventArgs e)
    {
        _application.RegisterViewEngines(ViewEngines.Engines);
        _application.RegisterRoutes(RouteTable.Routes);
    }
 
    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        // this ensures Default.aspx will be processed
        var context = ((HttpApplication) sender).Context;
        var relativeFilePath = context.Request.AppRelativeCurrentExecutionFilePath;
        if (relativeFilePath == "~/" || 
            string.Equals(relativeFilePath, "~/default.aspx", StringComparison.InvariantCultureIgnoreCase))
        {
            context.RewritePath("~/Home");
        }
    }
}

Application.cs

public class Application
{
    public void RegisterRoutes(ICollection<RouteBase> routes)
    {
        if (routes == null) throw new ArgumentNullException("routes");
 
        // default route
        routes.Add(new Route(
            "{controller}/{action}/{id}",
            new RouteValueDictionary(new { action = "Index", id = "" }),
            new MvcRouteHandler()));
    }
 
    public void RegisterViewEngines(ICollection<IViewEngine> engines)
    {
        if (engines == null) throw new ArgumentNullException("engines");
 
        SparkEngineStarter.RegisterViewEngine(engines);
    }
}

Adding Controllers and Views to project

Now add Content, Controllers, and Views folder. The following structure will be added to the web site:

Controllers\HomeController.cs

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

Views\Shared\_global.spark

<use namespace="System.Collections.Generic"/>
<use namespace="System.Web.Mvc.Html"/>
 
<global type="string" Title="'Alex'"/>

Views\Home\Index.spark

<p>Hello ${"World"}!</p>

Views\Layouts\Application.spark

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <title>${H(Title)}</title>
    <link rel="stylesheet" href="~/Content/Site.css" type="text/css" />
    <use content="head"/>
  </head>
  <body>
    <use content="view"/>
  </body>
</html>

The layout can be anything of course. This example is a very basic xhtml 1.1 shell which uses the Title variable the views may change and enables you to add view-specific stylesheets and javascript includes by placing them inside a <content name="head"> element. You'll bring your own file naming conventions to the Content directory, and structure the body with named content areas as appropriate.

The project can now "F5" to run. Hello World!

Windsor Inversion of Control container

Note - this example is using a build of castle off of the trunk. Specifics like the fluent registration interface may be different on in particular builds.

You'll need to add the following assembly references.

Change the Global.Application_Start to the following

        protected void Application_Start(object sender, EventArgs e)
        {
            var container = new WindsorContainer(Server.MapPath("~/Config/Windsor.config"));
 
            _application.RegisterFacilities(container);
            _application.RegisterComponents(container);
            _application.RegisterViewEngines(ViewEngines.Engines);
            _application.RegisterRoutes(RouteTable.Routes);
 
            ControllerBuilder.Current.SetControllerFactory(container.Resolve<IControllerFactory>());
        }

Add the following file

Config\Windsor.config

<configuration>
 
</configuration>

Add the following two methods to the Application class.

        public void RegisterFacilities(IWindsorContainer container)
        {
        }
 
        public void RegisterComponents(IWindsorContainer container)
        {
            container
                .Register(Component.For<IControllerFactory>()
                              .ImplementedBy<WindsorControllerFactory>())
 
                .Register(AllTypes.Of<IController>()
                              .FromAssembly(typeof (Application).Assembly)
                              .Configure(c => c.Named(c.ServiceType.Name.ToLowerInvariant())
                                                  .LifeStyle.Transient));
        }

And add the following WindsorControllerFactory class to your application.

WindsorControllerFactory.cs

    public class WindsorControllerFactory : IControllerFactory
    {
        private readonly IKernel _kernel;
 
        public WindsorControllerFactory(IKernel kernel)
        {
            _kernel = kernel;
        }
 
        public IController CreateController(RequestContext requestContext, string controllerName)
        {
            return _kernel.Resolve<IController>(controllerName.ToLowerInvariant() + "controller");
        }
 
        public void ReleaseController(IController controller)
        {
            _kernel.ReleaseComponent(controller);
        }
    }

A tutorial about the usage of Windsor is beyond the scope of this article. But at this point you should be able to "F5" and see your site run in exactly the same way it had before, but Windsor will be instantiating the transient instances of any controllers in the web application.

Using the Castle ILogger abstraction

Add references to

Register the facility in the Application class as follows

        public void RegisterFacilities(IWindsorContainer container)
        {
            container.AddFacility("logging", new LoggingFacility(LoggerImplementation.Trace));
        }

Add a public ILogger property to the controller. If you have a common base controller class in your project that's also an excellent place to add the property.

    public class HomeController : Controller
    {
        private ILogger _logger = NullLogger.Instance;
        public ILogger Logger
        {
            get { return _logger; }
            set { _logger = value; }
        }
 
        public ActionResult Index()
        {
            Logger.Debug("Doing lots of work in the Index action");
 
            return View();
        }
    }

Because we're using the LoggerImplementation.Trace the logging information will be directed to the dotnet System.Diagnostics.TraceSource classes. To provide that configuration add the following to the web.config.

web.config

<configuration>
  ...
  <!-- rely on an external file -->
  <system.diagnostics configSource="Config\Diagnostics.config"/>
  ...
</configuration>

Create that file to contain the diagnostic trace settings.

Config\Diagnostics.config

<system.diagnostics>
  <sources>
    <source name="Default" switchValue="Verbose"/>
  </sources>
</system.diagnostics>

"F5" to run the web application and the following will appear in Visual Studio's Output/Debug window.

Company.Alex.WebHost.Controllers.HomeController Verbose: 0 : Doing lots of work in Index action

By changing the Diagnostics.config you can direct information based on the class's full name, or part of the namespace, by the severity of the message, etc. Information can be sent to any of the System.Diagnostics trace listeners you configure. But again the full scope of this is beyond the scope of this article.