Story Details for articles

Golf Tracker - Ep. 6 - Presentation Layer and Unity

kahanu
Author:
Version:
Views:
3664
Date Posted:
8/24/2011 7:50:08 PM
Date Updated:
8/24/2011 7:50:08 PM
Rating:
0/0 votes
Framework:
ASP.NET MVC 2
Platform:
Windows
Programming Language:
C#
Technologies:
Unity
Tags:
golf, golf tracker, partial views, unity
Demo site:
Home Page:
Share:

Golf Tracker - Presentation Layer and Unity

In this episode I explain many aspects to what I need to do to prepare the presentation layer to receive and process data from the service layer and other sources.

Dependency Injection with Unity

There are many Inversion of Control (IoC) containers to help you with dependency injection.  In this application I've chose to use the Microsoft Practices framework called Unity.  Others include:
  • Ninject
  • StructureMap
  • Autofac
  • etc
To use Unity you need to first download the Unity framework (if you don't already have it).  You can download the latest version from http://unity.codeplex.com. Once you've downloaded and install Unity, you need to add references to the assemblies into your MVC application.  The assemblies you need to include are:
  1. Microsoft.Practices.EnterpriseLibrary.Common
  2. Microsoft.Practices.ObjectBuilder2
  3. Microsoft.Practices.Unity - version 1.2
To make my life easier integrating Unity into my MVC applications I've built a small library application that encapsulates much of the Unity plumbing so I can easily plug in my assembly and away I go.  It's called Unity.Library.Mvc2.  I've included it with the Golf Tracker Kit.

Here's what the project references look like.

Unity references

Unity Bootstrapper

To encapsulate the Unity functionality even further, and to make it easier and neater to refactor in the future, I've created a Bootstrapper class where all the mapping of concrete classes to interfaces will be handled.  Here's a look at the Bootstrapper class.

01.using System.Web.Security;
02.using GolfTracker.Api;
03.using GolfTracker.DataObjects.Interfaces;
04.using GolfTracker.DataObjects.Repositories;
05.using GolfTracker.Mvc.Web.Models;
06.using GolfTracker.Services;
07.using GolfTracker.Services.Interfaces;
08.using Microsoft.Practices.Unity;
09.using Unity.Library.Mvc2;
10. 
11.namespace GolfTracker.Mvc.Web.Unity
12.{
13.    public class Bootstrapper
14.    {
15.        private static IUnityContainer _container;
16. 
17.        private static void Configure(IUnityContainer container)
18.        {
19.            container
20.                .RegisterType<ICourseService, CourseService>()
21.                .RegisterType<ICourseRepository, CourseRepository>()
22.                .RegisterType<IPlayerService, PlayerService>()
23.                .RegisterType<IPlayerRepository, PlayerRepository>()
24.                .RegisterType<IRoundRepository, RoundRepository>()
25.                .RegisterType<ITeeRepository, TeeRepository>()
26.                .RegisterType<ITeeService, TeeService>()
27.                .RegisterType<IRoundService, RoundService>()
28.                .RegisterType<IRoundManager, RoundManager>()
29.                .RegisterType<IFormsAuthenticationService, FormsAuthenticationService>()
30.                .RegisterInstance<IMembershipService>(new AccountMembershipService(Membership.Provider))
31.                .RegisterType<IRoleService, RoleService>();
32. 
33.            UnityFactoryBuilder.Init(container);
34.        }
35. 
36.        public static void Configure()
37.        {
38.            if (_container == null)
39.            {
40.                _container = new UnityContainer();
41.            }
42.            Configure(_container);
43.        }
44.    }
45.}

(NOTE: this is the final implementation of the bootstrapper class with all the class mappings.  The video only shows those classes that are completed at this point.)

To make this work nicely, I need to include my Unity library which is called on line 33.  The UnityFactoryBuilder.Init(container) method creates the controller factory class that handles the resolving of the concrete classes to the interfaces.

Once this class is built, I simply reference it in the Global.asax class and I'm good to go.  Here's how it is used.

01.using System.Web.Mvc;
02.using System.Web.Routing;
03. 
04.namespace GolfTracker.Mvc.Web
05.{
06.    // Note: For instructions on enabling IIS6 or IIS7 classic mode,
08. 
09.    public class MvcApplication : System.Web.HttpApplication
10.    {
11.        public static void RegisterRoutes(RouteCollection routes)
12.        {
13.            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
14. 
15.            routes.MapRoute("GetCourses", "Course/GetCourses/{state}/{startsWith}", new { controller = "Course", action = "GetCourses", state = "", startsWith = "" },
16.                null, new string[] { "GolfTracker.Mvc.Web.Controllers" });
17. 
18.            routes.MapRoute(
19.                "Default", // Route name
20.                "{controller}/{action}/{id}", // URL with parameters
21.                new { controller = "Home",
22.                    action = "Index",
23.                    id = UrlParameter.Optional }, // Parameter defaults
24.                    null,
25.                    new string[] { "GolfTracker.Mvc.Web.Controllers" }
26. 
27.            );
28.        }
29. 
30.        protected void Application_Start()
31.        {
32.            GolfTracker.Mvc.Web.Unity.Bootstrapper.Configure();
33. 
34.            AreaRegistration.RegisterAllAreas();
35. 
36.            RegisterRoutes(RouteTable.Routes);
37.        }
38.    }
39.}

Line 32 contains the call to the Bootstrapper class.  That's all that's needed to configure Unity using my library helper.  This helps make the Global.asax cleaner so it doesn't contain all the mappings.

Also, the Bootstrapper class can also be in it's own project for easier deployment, but some other refactoring to your application would need to be done, but it is an option.

Setup your application

Now I want to finish setting up my application so I can quickly add functionality to it.  In order to do that I've borrowed some ideas from the KiGG application.  What these ideas help me do is implement a way of pulling common data that will be needed in either every controller call, or every view. 

MVC is built on top of the concept of "convention over configuration".  This means that specific conventions drive the framework, such as the name of a controller is also the name of the folder for the views it will display.

I also have incorporated various conventions in the way I build my MVC applications.  One of them is the use of view models for 99% of all view presentations.  In other words, I pass a ViewModel or ViewData class of some sort to the view and NOT simply the business object. This allows me to easily pass various pieces of data that may be needed for that view, such as data for dropdown boxes, or any other related information.

Following this thought, I've built my controllers to use custom ViewData classes for each business object that contains both a single business object, and a collection of business objects.

using System.Collections.Generic;
using GolfTracker.BusinessObjects;
 
namespace GolfTracker.Mvc.Web.ViewData
{
    public class CourseViewData : BaseViewData
    {
        public Course Course { get; set; }
        public List<Course> CourseList { get; set; }
        public double Avg { get; set; }
    }
}

You'll notice that this ViewData class (as does all others) inherits from a BaseViewData class.  This class can contain any common data that you want to pass up to the controller, and/or to the View.  More about this in a moment.

Side Note: another convention I use, primarily since I use a code-generator to create much of my CRUD operations for my application, is the way I handle the property name for collections.  In the example above the CourseViewData class has two methods for the Course business object, a single Course property and a List of Courses.  For the collections (List<Course>) my convention is to append the word "List" to the name of the business object.  This works out much better than just trying to append an "s", making it Courses.  This tends to muck up generated code from the code-generator unless you have an extremely intelligent Pluralizer.  Because while this will work for words like Course, it wouldn't work for words like News.  What's the plural of News, Newses??  So by having the naming convention for pluralizing names, I append the work "List".  (Another Note: this really only works if your naming convention for the business objects to begin with are singular.)

By creating a few different classes I'll be going from something like this in my controllers:

1.public ActionResult Index()
2.{
3.    CourseViewData viewData = new CourseViewData();
4.    viewData.CourseList = service.GetAll();
5. 
6.    return View(viewData);
7.}

... to this ...

1.public ActionResult Index()
2.{
3.    CourseViewData viewData = ViewDataFactory.CreateBaseViewData<CourseViewData>("Course List");
4.    viewData.CourseList = service.GetAll();
5. 
6.    return View(viewData);
7.}

Notice that I'm now calling a ViewDataFactory static class instead of simply creating a new instance of the view data class.  What this does, under the covers, is it pulls data from various sources, sets a base class with properties and sends them back up to the controller and eventually the view to be used as necessary.

The BaseViewData class is simply an abstract class that contains properties that will be populated by another class.

namespace GolfTracker.Mvc.Web.ViewData
{
    public abstract class BaseViewData
    {
        public string SiteTitle { get; set; }
        public string RootUrl { get; set; }
        public string MetaKeywords { get; set; }
        public string MetaDescription { get; set; }
        public string PageTitle { get; set; }
        public string IPAddress { get; set; }
    }
}

Here's a look at the folder structure for the ViewData folder.

view data folder

It's the BaseViewDataBuilder class that will populate the BaseViewData classes properties.

namespace GolfTracker.Mvc.Web.ViewData
{
    public class BaseViewDataBuilder
    {
        public static T CreateViewData<T>(string pageTitle) where T : BaseViewData, new()
        {
            T viewData = new T
            {
                SiteTitle = ConfigSettings.SiteTitle,
                RootUrl = Common.GetSiteRoot(),
                MetaKeywords = ConfigSettings.MetaKeywords,
                MetaDescription = ConfigSettings.MetaDescription,
                PageTitle = pageTitle
            };
            return viewData;
        }
    }
}

In this example, the values that are setting the properties are coming from the appSettings config file, but they can easily come from a database.

By using this methodology to create my ViewData or view model for the View, I am passing essential data up the stack without any repetitive work.  This also makes it easy to refactor what values I need to include by simply modifying the BaseViewData properties and then setting them in the BaseViewDataBuilder class.

Configuration

Another thing I do to setup my application is to extract out the appSettings and connectionStrings sections of the web.config.  By doing this it enables me to have settings and connection strings for development and a different set of settings for production.  So whenever I need to publish a new version of the application to the production server, I won't be overwriting any production settings with those for development.

Here's how I enable this in the web.config.

<appSettings configSource="settings.config"/>
<connectionStrings configSource="sql.config"/>

Here's a look at the files in the root of the project.

appsettings files

Then I can use a class to get the appSettings properties to use in the BaseViewData class.

Displaying the UI

Once all the action methods are created in the controller, it's easy to create the views.  But as you would imagine I have a certain convention for my basic CRUD views.

When it's possible I like to have a UserControl (Partial View) used to contain the actual form that is used for both the Create and Edit views.

course views folder

Here's the CourseFormControl partial view.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<GolfTracker.BusinessObjects.Course>" %>
 
    <% using (Html.BeginForm()) {%>
        <%= Html.ValidationSummary(true) %>
         
        <fieldset>
 
            <div class="editor-label">
                <%= Html.LabelFor(model => model.CourseName) %>
            </div>
            <div class="editor-field">
                <%= Html.TextBoxFor(model => model.CourseName, new { @class = "text" })%>
                <%= Html.ValidationMessageFor(model => model.CourseName) %>
            </div>
             
            <div class="editor-label">
                <%= Html.LabelFor(model => model.State) %>
            </div>
            <div class="editor-field">
                <%= Html.TextAreaFor(model => model.State, 10, 40, null) %>
                <%= Html.ValidationMessageFor(model => model.State) %>
            </div>
 
            <%= Html.HiddenFor(model => model.rowversion) %>
            <p>
                <input type="submit" value="Save" />
            </p>
        </fieldset>
 
    <% } %>
 
    <div>
        <%= Html.ActionLink("Back to List", "Index") %>
    </div>

And here is how it's used in the Create view.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/GolfTrack.Master" Inherits="System.Web.Mvc.ViewPage<GolfTracker.Mvc.Web.ViewData.CourseViewData>" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    <%= Model.SiteTitle %> - <%= Model.PageTitle %>
</asp:Content>
 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 
    <h2><%= Model.PageTitle %></h2>
 
    <% Html.RenderPartial("CourseFormControl", Model.Course); %>
</asp:Content>
 
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>

And the Edit view is nearly identical.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/GolfTrack.Master" Inherits="System.Web.Mvc.ViewPage<GolfTracker.Mvc.Web.ViewData.CourseViewData>" %>
 
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    <%= Model.SiteTitle %> - <%= Model.PageTitle %>
</asp:Content>
 
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
 
    <h2><%= Model.PageTitle %></h2>
 
    <% Html.RenderPartial("CourseFormControl", Model.Course); %>
</asp:Content>
 
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
</asp:Content>

For me, this makes it easier to make common modifications to the form and have it reflected on both views.  DRY!

Notice that in both cases, the RenderPartial HTML extension, passes in a Business Objects Course, instead of the entire CourseViewData class.  This is what the partial view is expecting, while the primary view is expecting other information such as the PageTitle and SiteTitle.

Conclusion

Now that the MVC application is setup (for the most part), the next thing I want to do is use some tooling that will help me generate controllers and views that are completely rendered exactly the way I need them for this application, instead of manually creating them every time.

In the next couple of episodes I'll demonstrate how to use and customize T4 templates to build custom controllers and views.

Stay tuned.

Comments

  • rchilambi@gmail.com
    Posted By: rchilambi
    On: 8/19/2014 12:17:33 PM
    One question is the MVC project (UI project) is dependent on the repositories project because it has to setup bindings. How do we avoid that?
  • info@kingwilder.com
    Posted By: King Wilder
    On: 8/19/2014 12:31:26 PM

    @rchilambi - I don't really understand what you are asking.  Dependency Injection removes the requirement of having concrete class references in your constructors, so Unity takes care of instantiating a concrete implementation of an interface for me.

    Also, there are no references to the Repository classes in the controllers, at least in my applications.  My controller only communicate with service layer implementations. 

    The MVC project is only dependent on interfaces, not implementations.

    Please explain what you want to avoid.  What I have described, is a best practice.

  • rchilambi@gmail.com
    Posted By: rchilambi
    On: 8/19/2014 12:38:29 PM

    Thanks for the quick response.

    I have done the same implementation, but here is the thing that bothers me.

    The mvc project is using bootstrapper class, which sets up the bindings to the repostitory classes needed by the service.  This means that UI project needs to have a project reference to the repositories project.  This is the dependency I was trying to see if this can be eliminated in some way.

  • info@kingwilder.com
    Posted By: King Wilder
    On: 8/19/2014 12:42:22 PM

    @rchilambi - I see what you mean.  That is a feature of Unity, at least that version of it.  As far as I know, you cannot simply have a reference to the services alone and then it will glean that it needs to continue on to the data objects.  That's why the data objects needs to be included in Unity.  Some other IoC applications might be able to continue down the dependency path, but I'm not sure which ones they are.

    It's a small issue though, not enough for me to spend any time try to overcome.  I don't really see a problem having those references in there.

  • rchilambi@gmail.com
    Posted By: rchilambi
    On: 8/19/2014 12:48:37 PM

    There are 2 ways to avoid it I was thinking but not sure if that is good practice.

    1.  Use a parameterless constructor that would setup the repositories that would be used by the mvc application.  Use a constructor that accepts these parameters which will be used by unit tests.

    2.  Do dynamic loading of the repository assembly based on configuration file setup in unity (not do it in bootstrap class and calling in application start)- this way you don't have to add references to the repositories project. Setup the configuration differently for unit test and mvc projects.

  • info@kingwilder.com
    Posted By: King Wilder
    On: 8/19/2014 1:12:26 PM

    @rchilambi - I don't think either of these ideas are a good practice, nor do I think it will enhance your application or get you anything.

    The way it's described in my article is already unit-testing-ready.  Since the MVC controller constructors simply take interfaces, you can easily substitute Fakes in your unit tests, and nothing needs to change in your MVC application.

    I would suggest that if you are really against having a reference to your repositories in your MVC application, create a separate WebApi project that contains all the services that you need, and simply have your MVC application call the WebApi service for all your processes.  That's an option.

 

User Name:
(Required)
Email:
(Required)