ASP.Net MVC Framework - How do I design my apps with the MVC pattern and +P
When I'm playing around with the ASP.NET MVC Framework I
have created several prototype applications with different
solutions to solve some "problems." In this post I will
write down how I combine a Presentation Model (When I talk
about Presentation Model in this post, I refer to a model
which purpose is only to define a model for presentation
purpose) with the MVC, it's what the P in MVC+P stands for.
When I build apps today I use Domain Driven
Design and I use the MVC pattern in some of my web based
applications. As many of you know the M in MVC stands for
the Model and contains our entities, business logic can data
access components. Often when I build my apps with the
ASP.Net MVC Framework (still only use in as a prototype
because the framework is under development) I create my own
custom Controller Factory to support Dependency Injection
(DI). For example I use constructor injection or setter
injection to inject my Repositories or manual injection of
Mock objects during test. I use Spring.Net as my DI
framework because it’s the framework I like the most.
Here is a simple example how my Controllers can look like and are prepared for constructor injection:
public class HomeController : Controller { ICompanyRepository _companyRepository; public HomeController() : this(new CompanyRepository()) { } public HomeController(ICompanyRepository companyRepository) { this._companyRepository = companyRepository; } public ActionResult Index() { CompanyInfo companyInfo = this._companyRepository.GetCompanyInfo(); return RenderView("Index", companyInfo); } }
As you can see I create an instance of a CompanyRepository
in the default constructor, the reason to this is because if
I create a normal instance of the HomeController, I want it
to use a default Repository. I also have a constructor which
takes a ICompanyRepository as an argument, this is because
to enable constructor injection. I can use the constructor
to pass in my Mock object of the CompanyRepository, or I can
use Spring.Net to inject a ICompanyRepository, which I use
in my own custom Controller Factory. Here is how my test
could look like where I have a Mock object for the
CompanyRepository:
MockCompanyRepository mockCompanyRepository = new MockCompanyRepository(); mockCompanyRepository.Create(new CompanyInfo("........")); CompanyController companyController = new CompanyController(mockCompanyRepository); var result = CompanyController.Index() as RenderViewResult; //Assert...
Note: If you have read my Step by Step Guide, I use the
LINQ to SQL's DataContext object directly in my
Controllers, it's because they can sometimes serve as a
"Repository" because the power of the LINQ.
When I use MasterPage which should always render a part of my Model, like content for a Menu etc, or when I need to render more than one entities of my Model, I need to make sure my Controllers will fill the ViewData property with more information than just one single entity from my Model. In this case I may have several Repositories which I need to call from my Controllers. If I use constructor injection, I need to add an argument to a Controller's constructor for each Repository. This can make the constructor cumbersome. To solve the issue and reduce the number of argument, I instead use setter injection, so I inject the Repositories needed through a set method (using properties). By doing this I can easy manually inject my Mock objects in my test:
MockCompanyRepository mockCompanyRepository = new MockCompanyRepository(); MockCustomerRepository mockCustomerRepository = new MockCustomerRepository(); //... CompanyController companyController = new CompanyController(); companyController.CompanyRepository = mockCompanyRepository; companyController.CustomerRepository = mockCustomerRepository; var result = CompanyController.Index() as RenderViewResult; //Assert...
I can still reuse my own custom Controller Factory without changes to support setter injection, because I simple only let Spring.Net know that I want to inject my Repositories by using setter injection. When I use setter injection, I don't create an instance of the default Repository or Repositories in the default constructor of the Controller.
The following is an example of a Controller which uses to Repositories and pass two entities to the "View":
public class HomeController : Controller { ICompanyRepository _companyRepository; ICustomerRepository _customerRepository; public HomeController() { } public ActionResult Index() { ViewData["CompanyInfo"] = this.CompanyRepository.GetCompanyInfo();
ViewData["Customer"] = this.CustomerRepository.GetCustomer(); return RenderView(); } public ICustomerRepository CustomerRepository { get { if (this._customerRepository == null) this._customerRepository = new CustomerRepository(); return this._customerRepository; } set { this._customerRepository = value; } } public ICompanyRepository CompanyRepository { get { if (this._companyRepository == null) this._companyRepository = new CompanyRepository(); return this._companyRepository; } set { this._companyRepository = value; } } }
The Index method will now return two entities. Assume the CompanyInfo is for the MasterPage and the Customer for the Content Page. The ViewData property in this case will sort of be a Presentation Model ("The essence of a Presentation Model is of a fully self-contained class that represents all the data and behavior of the UI window") but don't contain any behavior.
Something that can make the above Controller or other Controllers ugly and start to smell, is when most action method must set the ViewData for the Model which the MasterPage will render. There is a tiny little problem regarding to how I will work and think when it comes to Test Driven Developement, when I use a MasterPage or User Controls on a View which will render different models. The problem is, when I create a test for a Controller before the Controller is implemented, I must know about what Models the UI will render, all entity objects even for the MasterPage and User Controls. I can't only assume that the CustomerController's List action method will return a list of Customers, I also need to have in mind the model the MasterPage and User Controls will use and when writing the test. Well basically I can ignore it, but it can affect how my ViewData will look like, and what key I need to use to get access to my list of Customers etc. This is something that I don't want to care about for every action method I want to create a test for. I also like the way I can type the ViewData property for the View. To solve the issue I decided to create a base class called PresentationModel. This class have one typed property for the model the MasterPage should render, MasterPageModel and one for the Repository which will get the Model for the MastePage, here is an example:
public class PresentationModel { private CompanyInfo _companyInfo; public CompanyInfo MasterPageModel { get { if (this.CompanyRepository == null) throw new ApplicationException("A ICompanyRepository must be set"); if (this._companInfo == null)
this._companyInfo = this.CompanyRepository.GetCompanyInfo(); return this._companyInfo; } set { this._companyInfo = value; } } public ICompanyRepository CompanyRepository { get; set; } }
If the Model or the MasterPage is changed and that
affect what the MastePage should render, I only need to
change what model the PresentationModel class returns
through the MastePageModel property, I don't need to go
through all action methods and update them. That is one
benefit with this design solution.
Note: I use a property for the CompanyRepository to
make it easy to add a Mock object in my tests.
The PresentationModel is the type I specify
for the MastePage to make it typed.
public partial class Site : System.Web.Mvc.ViewMasterPage<PresentationModel> { }
I then create a sub class of the PresentationModel
called GenericPresentationModel. The
GenericPresentaionModel class is a generic class which
inherits from the PresentationModel and have a generic
property called Model.
public class GenericPresentationModel<T> : PresentationModel { public GenericPresentationModel() {} public GenericPresentationModel(T model) { this.Model = model; } public T Model { get; set; } }
The Controllers can now use the
GenericPresentationModel and pass it to the View, the
following Controller has an Action method which will
render a Customer:
public ActionResult Index() { Customer customer = this.CustomerRepository.GetCustomer(); var presentationModel = new GenericPresentationModel<Customer>(customer); return RenderView("Index", presentationModel); }
Because I pass the GenericPresentationModel to my
View, I can also type the View:
public partial class CustomerView : ViewPage<GenericresentationModel<Customer>> { }
Then my ViewData property of my View will be of Type
GenericPresentationModel<Customer>. So I can easy
get access to my Customer by using ViewData.Model.
The following example shows how the test of an
Action which will render a Customer could look like:
MockCustomerRepository mockCustomerRepository = new MockCustomerRepository(); //... HomeController controller = new HomeController(); controller.CustomerRepository = mockCustomerRepository(); var result = controller.Index() as RenderViewResult; var presentationModel = result.ViewData as GenericPresentationModel<Customer>; //Assert.....
Because I don't use the MastePageModel property in
my test of the Index method, I don't need to inject the
Mock Repository for the PresentationModel base class. When
I created the PresentationModel I have already created a
test to make sure the PresentationModel returns the
correct data, so I don't need to care about testing that
again in my other tests.
I still use this approach in a prototype solution to see
how well it will work. When I work with Domain Driven
Design I often need to render different information from
different entities from my domain model. In my case I
don't always want to pass several entities to my Views, so
instead I create new object that will combine information
from different entities, with another word, I create a
Presentation Model. Most larger apps which I have created
with the ASP.Net MVC Framework, often result in the use of
a Presentation Model, and that is the P in the MVC+P. I
know that several people don't like this because it
requires us to create a lot of extra classes, but the
benefits is that the Views will use the Presentation Model
and don't even need to know about the domain model. If we
don't pass a single entity from our domain model to the
View, instead we use a Presentation Model and map the
domain model to the Presentation Model, we can change the
domain model, and it doesn't need to change the Views,
only the mapping between the Presentation Model and the
domain model. To be honest I don't create a complete
Presentation Model, I still pass entities from the domain
model to the View, mostly to keep it simple and not spend
extra time to write new classes for each View.
I'm interested about what you think, how you
solve similar problems, do you use similar solutions that
I do and have notice problems that I haven't notice yet,
please let me know.