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.

2 Comments

  • Are your controllers thread-safe? Spring.net creates singletons by default, so it will return the same instance of controller for every request. I like Spring.net too, but in this case it increases the complexity of code.

    I think it's not appropriate to use DI for construction of short-lived objects like controllers. I prefer another intermediate object between the controller and the repository that can be used to find the repositories - the registry. It looks like this:


    public class RepositoryRegistry
    {
    static RepositoryRegistry _instance;
    public static RepositoryRegistry Instance
    {
    get { return _instance; }
    set { _instance = value; }
    }

    public ICompanyRepository Company { get; set; }
    .... more repositories ....
    }



    RepositoryRegistry instance is contructed by Spring.net at application startup, so that controller methods can find the repositories they need:


    RepositoryRegistry.Instance.Company



    It's equally testable and mockable (you just setup the RepositoryRegistry before the test) and it reduces the repeating code, since all the repository properties are declared at one single spot. You don't have to insert the repository-holding properties to every controller that needs them, since they are already accessible in well-known registry object. With this approach you don't even need the ControllerFactory.


  • Dkl:
    Good comment!
    I have an old post about a solution that will probably look like yours, here it is (Note: I don't use the name Repository in the post, because this post is based on a project I helped a customer with, and they have already decided to use something else):
    fredrik.nsquared2.com/ViewPost.aspx
    This was the first solution I thought of using again. It’s not a bad solution at all. The only thing is that the Controller in this case will need to use the “RepositoryRegistry”, and we need to define all the Repositories into the Register. If we add a new one later on, we need to add it to the Register. Well nothing strange nothing wrong with that. The only thing I want to do is to make sure a Controller only get access to the Repositories it needs to use. So I don’t pass in an object with gives the Controller access to Repositories it shouldn’t use in the first place. To set the limit and restriction, I decided to use set method, each Controller has a property, where I can pass in my Repositories it should use. But something that I notice lately when the project increases, was that I need some extra configuration for each Repository, and that start to killing me. So I’m actually thinking of going back to my old previous well tested “RepositoryContext” solution. And you made me think more about doing that, so THANKS for your comment!

Comments have been disabled for this content.