jb's Blog


"behold the turtle, he only makes progress when he sticks his neck out"

MVC and LightSpeed – Part 3 – Setting up the Site

tag icon Tagged as LightSpeed, MVC, jQuery

In our earlier instalments we set up our domain model for the Film Festival as well as a unit test project (currently for the model, but we will extend that for the web site soon enough..). Now its time to create our MVC site.

Create the Web

First step is to set up an ASP.NET MVC site instance called “Web”, this is easily achieved using the VS template if you have it installed.

Shot013

By default, the template includes quite a few things I don’t really care for such as an out of the box set of styles and some of the Microsoft AJAX scripts – I get away with just jQuery. You will notice that the current latest version of jQuery is installed with the RTM templates (1.3.2), however this will soon be updated so make sure you check for these on jQuery.com regularly enough. The focus of this current line (1.3.x) seems to be focused on really tuning the performance of the library so you will likely benefit from upgrades :)

Here is what my structure looks like at the end of some quick culling..

Shot014

I have obviously added in references to the Model project and LightSpeed, and I have added the Web project as a reference for our UnitTests.

Cue the pre-canned stuff

Given that I am updating my existing FilmFestival site – I do already have some of the creative taken care of :)   Cue the one I baked earlier, and our site now looks like this – I have included an existing style sheet and images, and then modified Site.Master and Views/Home/Index.aspx to make this happen.

Our site is now taking shape visually which is always motivating I find..

Shot015

What we now want to do is set up the home page so that it displays a list of our locations, a sample selection of films on the side bar and for it to display the first film as our “feature film” showing its photo and the name of the film.

Controllers

The process logic for our web application is executed in the Controller actions. The paradigm with MVC is we set up a routing table which will map incoming requests to controller actions based on the URL which has been requested.

The default routing table included with our MVC site is this:

    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 = "" }  // Parameter defaults
      );
 
    }

You can see that currently our HomeController::Index() action is pretty thin – nothing wrong with that, Thin Controller / Thick Model is generally what we are aiming for, however what we do need it to do is to fetch us a list of locations and some films, and to do that we need to fetch them through our LightSpeed unit of work.

Managing the LightSpeed Unit of Work on a Per Request basis

When using LightSpeed the UnitOfWork manages interactions with your entities inside a scope, usually about the size of a business transaction. On the web, your transaction is likely to be the scope of a single request so ideally we want a unit of work to be set up at the beginning of a request and then to be disposed at the end. We can set up this approach using the PerRequestUnitOfWorkScope<TUnitOfWork> class, which is an implementation of UnitOfWorkScopeBase<TUnitOfWork> and will hold us an instance of TUnitOfWork which in our case will be our strongly typed UnitOfWork class which was generated for us when we set up the domain model earlier and has the IQueryable properties for our entity collections making querying a bit easier through LINQ.

To help set this up, we are going to create a base controller class for our project called FilmFestivalController which implements Controller. All of our specific controllers such as HomeController will implement this.

Here is the code we are going to add:

    private const string UNIT_OF_WORK = "__UnitOfWork__";
    private UnitOfWorkScopeBase<FilmFestival.Model.UnitOfWork> _unitOfWorkScope;
 
    public UnitOfWorkScopeBase<FilmFestival.Model.UnitOfWork> UnitOfWorkScope
    {
      get
      {
        if (_unitOfWorkScope == null && System.Web.HttpContext.Current != null)
        {
          _unitOfWorkScope = System.Web.HttpContext.Current.Items[UNIT_OF_WORK] as PerRequestUnitOfWorkScope<FilmFestival.Model.UnitOfWork>;
        }
 
        if (_unitOfWorkScope == null)
        {
          _unitOfWorkScope = new PerRequestUnitOfWorkScope<FilmFestival.Model.UnitOfWork>(MvcApplication.LightSpeedDataContext);
 
          if (System.Web.HttpContext.Current != null)
          {
            System.Web.HttpContext.Current.Items[UNIT_OF_WORK] = _unitOfWorkScope;
          }
        }
 
        return _unitOfWorkScope;
      }
    }
 
    protected FilmFestival.Model.UnitOfWork UnitOfWork
    {
      get { return UnitOfWorkScope.Current; }
    }
 
    protected override void OnResultExecuted(ResultExecutedContext filterContext)
    {
      if (_unitOfWorkScope != null)
      {
        _unitOfWorkScope.Dispose();
      }
 
      base.OnResultExecuted(filterContext);
    }
 
    public new void Dispose()
    {
      if (_unitOfWorkScope != null)
      {
        _unitOfWorkScope.Dispose();
      }
 
      base.Dispose();
    }

Basically we are facilitating a Festival.Model.UnitOfWork property on our controllers which will be bound to the current unit of work for this request – otherwise one will be instantiated as required by calling .CreateUnitOfWork on the LightSpeedContext (gives context on how we are connecting to the underlying database) which is a static object we have declared in our Global.asax

internal static readonly LightSpeedContext<FilmFestival.Model.UnitOfWork> LightSpeedDataContext 
  = new LightSpeedContext<FilmFestival.Model.UnitOfWork>("default");

You can see that we also implement an override for OnResultExecuted which will fire once the ActionResult has been processed which disposes the unit of work.

If we take an approach of not passing entities through to the views where the unit of work may possibly be needed to reference lazy child collections and the like then we could dispose those earlier, however you may find it more sensible to allow yourself this convenience in your view – but be aware you may be generating additional database request (== costly).

Fetching what we need for the Home View

Getting information to the view can be done in one of two ways, either we can use the generic ViewData property bag – e.g. ViewData[“Key”] = value; or we can create a strongly typed ViewModel class which we pass in place of the generic ViewDataDictionary instance. I currently prefer the latter approach because it is a little clearer about what needs to be sent to the view and I also find myself re-using these view models across a number of views which can also be helpful. The downsides I have noticed so far is that often your ViewModel may be specific to the view, so you either end up with a large ViewModel which is being shared around but some properties are never set which means you possibly should just go back to the property bag :) YMMV so keep both options in mind.

So I want to quickly create a ViewModel for our Index action, nothing hard here, I create a class called HomePageViewModel.cs and put it under a ViewModels folder, it is going to look like this:

public class HomePageViewModel
{
  public IList<Film> Films { get; set; }
  public IList<Location> Locations { get; set; }
  public Film FeaturedFilm { get; set; }
}

We can fetch our data by modifying the Index() action as follows

IList<Film> films = UnitOfWork.Films.ToList().OrderBy(o => Guid.NewGuid()).Take(8).ToList();
 
HomePageViewModel model = new HomePageViewModel()
{
  Locations = UnitOfWork.Locations.ToList(),
  Films = films,
  FeaturedFilm = films[0]
};
 
return View(model);

You can see how we can use the IQueryable properties on UnitOfWork as entry points for our queries, you can use LINQ syntax to structure up a query to fetch your entities as required – for example my “random” selection fetches all Films into memory and then sorts them randomly using GUIDs and then selects the top 8 for display.

Applying this to the View

I am currently using the default WebFormViewEngine (ASP.NET Web Forms) for the views, so I have .aspx files to work with. One of the nice things about ASP.NET MVC is that I am not limited to just using web forms. If I dont like it I could use something like NHaml, which is currently my preferred view engine, however that is kind of a tangent so we will stick with web forms for now.

Firstly I want to use my typed view model, so I will declare a local variable on the form called Model to hold this

<% var Model = (Web.ViewModels.HomePageViewModel)ViewData.Model; %>

This is a bit naff – There undoubtedly is a better way but my ignorance in the WebFormViewEngine space has left me a bit naive here. With NHaml we have our typed models available so I wouldn’t need to declare this.

The next step is to expand out our view code to make use of the Model we have available to wire up our required entities for display. e.g.

        <h3>
          <a href="/films">Explore Films</a>
        </h3>
        <ul>
            <% 
              foreach (var film in Model.Films)
              {
            %>
              <li>
                <a href=<%= String.Concat("/film/", film.Id) %>>
                  <%= film.Title %>
                </a>
              </li>
            <%
              }
            %>
        </ul>

Easy enough. I am likely to want to refactor how those links are getting generated however because I know we can make use of some of the HtmlHelper utilities to generate these – we can refactor this once we have built out actions for these targets.

Look at the Home Page now

So with our model wired up, lets re-run our site – and here is what it now looks like

Shot016 

So that’s a great start, the only thing I probably want to add to this is a bit of Output caching to avoid generating unnecessary work on the server – we can do this easily in MVC by applying an [OutputCache(Duration=60, VaryByParam="")] attribute on the top of our Index action. How can we tell its working? Well previously the home page would likely display a new film after each refresh, now it does not. The [OutputCache] attribute is an example of an action filter, we will delve more into these at a later time.

Playing along at home?

Here is the code and associated database setup script (remember to create a database called FilmFestival and run the scripts under the context of that database first or use the Build.cmd script) to cover what we have done so far. Also remember to download and install LightSpeed.

Download FilmFestival2009_Part3.zip (538KB)

Ok – What’s next?

Next we can look at setting up a form where we want to unbind the data into a LightSpeed entity and flesh out some functions of the site a bit more.

8 Responses to “MVC and LightSpeed – Part 3 – Setting up the Site”

  1. for the typed .aspx views, why not to use:
    <%@ Page Inherits=”System.Web.Mvc.ViewPage” Language = “C#”

    or whatever namespaces you are using, I’ve just given a glance on the articles, hope the architecture will improve over the time to be more DDD though :)

    and to incourage DDD, I thought you will start with the Domain Model… starting with the DB (although it is 1:1 mapping) really killed the articles for me (I really _don’t_know what DB is in the first place, hence O/RM at some place).

  2. well, it has been filtered so there should be:

    Mvc.ViewPage” Language = “C#”

  3. ViewPage[Cinema.Models.HomePageViewModel]” Language = “C#”

    with angle brackets…

  4. Great – will do :) As I mentioned, I am a bit naive on the web forms view side of the world – I have been too long in Haml land ;)

  5. I started with the DB because I already had it :) Part of the motivation for this was my slackness in getting the original sample out ;)

  6. ASP.NET MVC Tutorial Part 1: Creating the website…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  7. [...] to Vote[FriendFeed] ASP.NET MVC Tutorial Part 3: Creating the website (3/20/2009)Friday, March 20, 2009 from [...]

  8. [...] to VoteJeremy Boyd: MVC and LightSpeed – Part 3 – Setting up the Site (3/18/2009)Wednesday, March 18, 2009 from http://www.mindscape.co.nz…the web site soon enough..). Now its time to [...]

Leave a Reply

jb's Blog