Pages

Tuesday, 27 March 2012

MVC3 View Models


I keep seeing a familiar question on message boards - How do I return multiple models to a view? I can see why this question is asked; traditionally with ASP.NET Web Forms applications, you may have a page which has multiple gridviews displaying data from different sources in your application. Part of the learning curve for MVC3 for Web Forms developers is understanding the relationship between the model and view, and how to still use one model for displaying data from different sources. This is where view models come into play.

For example, you may have a default page in your MVC3 application that needs to display a list of products and latest news. For this simple and common example, you will need:

  • two model entities to represent a news article and a product
  • two repositories in your data access layer 
  • a view model to use with your controller

Your news article entity may be something like:

namespace Domain.Entities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

 
    public class News
    {
        public int ID { get; set; }

        public string Headline { get; set; }

        public string Article { get; set; }

        public DateTime Date { get; set; }

    }
}

Your product entity may be something like:

namespace Domain.Entities
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;


    public class Products
    {
        public int ID { get; set; }

        public string Name { get; set; }

    }
}

In your data access layer, you should have a repository for news articles and products. Each of these repositories have methods to return data from your data store. In both the repository examples below, generic lists are returned from a method in each repository call "GetAll".

namespace Domain.Repositories
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Domain.Entities;
    using System.Data;

public class NewsRepository
    {

        public List<News> GetAll()
        {
            List<News> articles = new List<News>();
            // Call to database here to return data and add news entities to the articles generic list
            return articles;

        }

    }
}

namespace Domain.Repositories
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Domain.Entities;
    using System.Data;

public class ProductsRepository
    {

        public List<Product> GetAll()
        {
            List<Product> products = new List<Product>();
            // Call to database here to return data and add product entities to the products generic list
            return products;

        }

    }
}

You will then need a view model to combine the list of products and news articles you want to display on your default page:

namespace BusinessLogic.ViewModels
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Domain.Entities;
    using Domain.Repositories;
    using Business_Logic.ViewModels;

public class Home
    {

        private List<News> articles
        private List<Product> products;

        public List<News> Articles
        {
            get
            {
                if (articles != null)
                {
                    return articles;
                }
                else
                {
                    return null;
                };
            }
        }
        public List<Product> Products
        {
            get
            {
                if (products != null)
                {
                    return products;
                }
                else { return null; };
            }
        }


        public Home()
        {
            var articleDetails = new NewsRepository();
            articleDetails = articleDetails.GetAll();
            articleDetails = null;

            var productsList = new ProductsRepository();
            products = productsList.GetAll();
            productsList = null;
        }
    }
}


The above view model is just a regular class, with nothing special about it. It has two public properties with backing fields that are populated when the class is instantiated. One property exposes a list of news articles, the other a list of products. This view model can then be passed by your controller to your view.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BusinessLogic.ViewModels;

namespace WebUI.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {

            var viewModel = new Home();
            // The instantiation populates the backing fields in the Home view model and these are now available via the public properties
            return View(viewModel);
        }

    }
}


In your view, you can now reference the generic list of products and the generic list of news articles to display in your view:

@model BusinessLogic.ViewModels.Home
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
@foreach (var p in Model.Articles)
{
    @p.Headline
}


@foreach (var p in Model.Products)
{
    @p.Name
}

Having been through the curve with learning MVC3 from Web Forms, I hope this helps someone. The examples above are quite simple, and I've avoided using dependency injection for simplification and hopeful ease of understanding view models.

No comments:

Post a Comment