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
- Microsoft.Practices.EnterpriseLibrary.Common
- Microsoft.Practices.ObjectBuilder2
- Microsoft.Practices.Unity - version 1.2
Here's what the project references look like.
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,
07.
// visit http://go.microsoft.com/?LinkId=9394801
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.
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.
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.
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.
@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.
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.
@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.
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.
@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.