8 Mar
2009

FubuMVC From Scratch – Part 2

Category:UncategorizedTag: , , :

In my previous post I talked about setting up the project structure for a FubuMVC app. If you haven?t checked that one out, I would recommend doing that first. I should also point out that this is probably the last post I will be using ?FubuSample? in. I have my college class working on an open source shopping cart built on FubuMVC, called FubuCart. I will start using it as a my sample, and you will be able to download the source code from the Google code project.

Goal for this part of the series: Get a View up and running in our sample app.

 We will need to have several things in our projects before we can make our FubuSample app come to life:

  • Web Registry ? This is how we tell StructureMap what and how to build some things we need.
  • Bootstrapper ? This kick starts FubuMVC
  • Global.asax ? This is where all of our conventions and overrides for how we want things to work go.

Web Registry

First up is the web registry, go ahead and add a class to the FubuSample.Web project named FubuSampleWebRegistry. This class should extend the Registry class found in StructureMap.Configuration.DSL. You also need to generate a default (empty) constructor. We will use this to put all of our configuration in later as we expand on this project.

using StructureMap.Configuration.DSL;
 
namespace FubuSample.Web
{
    public class FubuSampleWebRegistry : Registry
    {
        public FubuSampleWebRegistry()
        {
            
        }
    }
}

Bootstrapper

Add another class to the Web project, named Bootstrapper. This class will implement StructureMap?s IBootstrapper interface, which is just a public void method named BootstrapStructureMap(). The code for this class is mostly copied from the AltOxite sample, but let?s take a look at it and see what is going on. Also, you will need to add a reference to System.Web.Routing for this step.

public class Bootstrapper : IBootstrapper
    {
        private static bool _hasStarted;
 
        public void BootstrapStructureMap()
        {
            ObjectFactory.Initialize(x =>
            {
                x.AddRegistry(new FrameworkServicesRegistry());
                x.AddRegistry(new FubuSampleWebRegistry());
                x.AddRegistry(new ControllerConfig());
            });
 
            ObjectFactory.AssertConfigurationIsValid();
 
            setup_service_locator();
 
            apply_action_conventions();
 
            initialize_routes();
        }

First up we have our interface method BootstrapStructureMap(). This method is only doing a few things as we can see. First up we are Initializing StructureMap with a call to ObjectFactory.Initialize. In this call we are adding a new FrameworkServicesRegistry, our own FubuSampleWebRegistry, and ControllerConfig registry. Registry items 1 and 3 both come from the FubuMVC framework, and we just added the FubuSampleWebRegistry.

Once this is done we are making calls to some of our own private methods.

private static void setup_service_locator()
{
    ServiceLocator.SetLocatorProvider(() => new StructureMapServiceLocator());
}

setup_service_locator is simply telling the ServiceLocator who the IOC provider is for this project.

private static void initialize_routes()
{
    ObjectFactory.GetInstance<IRouteConfigurer>().LoadRoutes(RouteTable.Routes);
}

initialize_routes() is grabbing the configured instance of IRouteConfigurer from FubuMVC and is loading up the Route Table from the System.Web.Routing class.

private static void apply_action_conventions()
{
    var fubuConfiguration = ObjectFactory.GetInstance<FubuConfiguration>();
    var actionConventions = ObjectFactory.GetAllInstances<IFubuConvention<ControllerActionConfig>>();
 
    fubuConfiguration.GetControllerActionConfigs().Each(actionConfig =>
        actionConventions.Each(conv =>
            conv.Apply(actionConfig)));
}

apply_action_conventions is a recent addition to FubuMVC. What this little gem does is add the application of conventions defined on action configs

into the Bootstrapper.  After StructureMap is configured, the

conventions for ControllerActionConfig are applied (making whatever

changes are appropriate per the convention(s)) and the app is then ready

to run.

And finally two methods to wrap up this class. These two methods are helper methods.

public static void Restart()
{
    if (_hasStarted)
    {
        ObjectFactory.ResetDefaults();
    }
    else
    {
        Bootstrap();
        _hasStarted = true;
    }
 
}
 
    public static void Bootstrap()
    {
 
        new Bootstrapper().BootstrapStructureMap();
    }
}

Before we move onto the Global.asax file, we need to create a class in FubuSample.Core.Web namespace called ?ViewModel?. The code for this class is below, every Input and Output ViewModel we use in our controllers will inherit from this class, so we put properties in here that are global or site wide.

public class ViewModel
{
    public string SiteName { get; set; }
    public string LanguageDefault { get; set; }
    public string SEORobots { get; set; }
}

Global.Asax

The next thing our web app is going to need is a Global.asax file. Inside this file is where we will do the majority of our FubuMVC configuration. So go ahead and add a Global.asax file to your FubuSample.Web project.

This file will have a lot of methods in it once you open it, the only one we are really interested in at this point is the Application_Start method. In order to keep this simple, we will only add the absolute minimum things required to make FubuMVC work. There are several things we can do with FubuMVC inside the ControllerConifg.Configure expression, we can setup Using Conventions and ActionConventions, these are site wide conventions that will apply to every item of their respective application.

Initially we just need to tell the app that every controllerAction will Execute the Result and where to find our controllers we will make.

protected void Application_Start(object sender, EventArgs e)
{
    ControllerConfig.Configure = x =>
     {
         x.ByDefault.EveryControllerAction(d => d
             .Will<execute_the_result>());
 
         x.AddControllersFromAssembly.ContainingType<ViewModel>(c =>
            {
                c.Where(t => t.Namespace.EndsWith("Web.Controllers")
                             && t.Name.EndsWith("Controller"));
 
                c.MapActionsWhere((m, i, o) => true);
            });
     };
 
    Bootstrapper.Bootstrap();
}

At this point we have done the minimum required configuration to use FubuMVC. What we need to do next is create a simple object in our domain and hook a controller and a view up to it. I created a simple object named Product in my FubuSample.Core.Domain namespace.

public class Product
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

As you can see, this class is very simple, but that is all we need for now.

Setting up a Controller

Let?s create a class in our Core.Web.Controllers folder named ?HomeController?. One of the key principals for FubuMVC is the Thunderdome Principal, or OMIOMO, One Model In One Model Out. So in order to make our controller function we will need to add a method named the same as our View which will be Index. Now our Index method should take an InputViewModel in the constructor and should return an OutputViewModel. We will need to make our respective ViewModel classes once we setup our controller action.

Our Index action looks like this at the moment:

public IndexViewModel Index(IndexSetupViewModel inModel)
{
    return new IndexViewModel();
}

Now we need to create two ViewModel derivatives, one for our input, and one for our output. Now all we are attempting to do with our Home/Index view is to show a list of products. So we will make our output viewmodel, IndexViewModel have a public IEnumerable<ProductDisplay> property.

public class IndexSetupViewModel : ViewModel
{
}
 
[Serializable]
public class IndexViewModel : ViewModel
{
    public IEnumerable<ProductDisplay> Products { get; set; }
}

You probably already noticed that I did not setup my list of products to use the domain object named Product that we just created. Instead I am using a display model named ProductDisplay. For the purpose of this sample we probably could have just used Product, but I really don?t like to pass domain objects directly to the view unless absolutely necessary. So create a new class named ProductDisplay in the DisplayModels folder in your Core.Web folder. Your class should look like this when it is done.

public class ProductDisplay
{
    public ProductDisplay(Product product)
    {
        Name = product.Name;
        Description = product.Description;
    }
 
    public string Name { get; set; }
    public string Description { get; set; }
}

So we now have our ProductDisplay class but we haven?t populated our IEnumerable on our IndexViewModel, so let?s make a hard coded list of products in our IndexAction. Our modified IndexAction now looks like this, I know some of the code is ugly with building our list, but hey this is a sample.

public IndexViewModel Index(IndexSetupViewModel inModel)
{
    var outModel = new IndexViewModel();
 
    var productList = new List<ProductDisplay>();
 
    productList.Add(new ProductDisplay(new Product { Name = "TestProduct1", Description = "This is a test product"}));
    productList.Add(new ProductDisplay(new Product { Name = "TestProduct2", Description = "This is a test product"}));
    productList.Add(new ProductDisplay(new Product { Name = "TestProduct3", Description = "This is a test product"}));
 
    outModel.Products = productList;
 
    return outModel;
}

This post has gotten long enough and there are a few more steps required to get our View running, so I am starting a new post. Check back real soon, I will finish it tonight.

As always, I do these posts as much for discussion as anything, so if I did something wrong, or something you have a better way of doing, please let me know.

-Ryan

6 thoughts on “FubuMVC From Scratch – Part 2

  1. Kazi,

    This is a very good point. This discussion actually just came up yesterday, but I was already past that part of the post so I didn’t do it here. But I totally agree, all the stuff should move out into the bootstrapper.

  2. Hello,

    In

    private static void apply_action_conventions()
    {
    var fubuConfiguration = ObjectFactory.GetInstance();
    var actionConventions = ObjectFactory.GetAllInstances<IFubuConvention>(); fubuConfiguration.GetControllerActionConfigs().Each(actionConfig => actionConventions.Each(conv => conv.Apply(actionConfig)));
    }

    Compiler complains about the .Each, is there a using I am missing?

    Similarly in the Application_Start, the compiler complains about

    x.AddControllersFromAssembly.ContainingType

    AddControllersFromAssembly

    Is there a reference or a using I am missing?

    TIA
    Yaz

  3. @Yazid
    Add using directive to FubuMVC.Core in the bootstrapper.cs. The IEnumerable Extension is defined there.

Comments are closed.