There seems to be much confusion about the usage of typed views and ViewModel pattern in ASP.NET MVC. What it is, how it came about and when best to use it? In this article I discuss how the ViewModel pattern came about, what problem it addresses and how it can be implemented in ASP.NET MVC. In later blog posts I will also discuss a few of the variants on the ViewModel pattern, their use cases, their benefits and their disadvantages.
Let’s say we are developing a blog application. For the home page – or the Index view – we display a list of Posts along with the links for the most popular posts. Here are the models for our scenario. I am using EF 4 Code First as my ORM solution here.
- public class Post
- {
- public int ID { get; set; }
- public string Title { get; set; }
- public string Body { get; set; }
- public string CreatedBy { get; set; }
- public string CreatedOn { get; set; }
- public ICollection<Comment> Comments { get; set; }
- }
- public class Comment
- {
- public int ID { get; set; }
- public string Name { get; set; }
- public string Email { get; set; }
- public string Url { get; set; }
- public string Body { get; set; }
- }
- public class PopularPost
- {
- public int ID { get; set; }
- public Post Post { get; set; }
- // The number of times the post was viewed/read.
- public long ViewedCount { get; set; }
- }
ViewData pattern
One option available to us out of the box is the usage of ViewData both in our controller action method and in our aspx views. So the usage might result in our Controller action method as follows,
- public class PostController : Controller
- {
- private readonly SimpleBlogDb _simpleBlogDb = new SimpleBlogDb();
- public ActionResult Index()
- {
- // Get the top 10 most recent posts.
- var posts = from d in _simpleBlogDb.Posts
- orderby d.CreatedOn ascending
- select d;
- ViewData["Posts"] = posts.Take(10);
- // Get the top 10 most read posts.
- var popularPosts = from p in _simpleBlogDb.PopularPosts
- orderby p.ViewedCount descending
- select p;
- ViewData["PopularPosts"] = popularPosts;
- return View();
- }
- }
Then in our Index view, we might implement it as follows:
- <% foreach (var post in ViewData["Posts"] as IEnumerable<Post>){%>
- <p><%= post.Body %></p>
- <%}%>
Disadvantages of ViewData
One disadvantage of this approach is that the use of “magic strings” such as “Posts”, “PopularPosts” could be a source of Null Reference exception errors. The most frequent common cause for this type of error is a typo when setting or getting values from ViewData using these “magic strings”.
Most of us developers tend to overlook – initially – the fact that the source of our errors is a simple typo. Only after going through a few debugging session do we realize these errors – albeit with much chagrin. The more frequently we use these “magic strings” as variables in our application the more chances we increase of such bugs and thus compound negatively our productivity.
In addition, changing the values of these “magic strings” is also difficult once they are used in our views, controllers and or view helpers. In our scenario, the “magic strings” would also propagate in our create, edit, and details views thus making our solution brittle to change and refactoring and increasing possibilities of errors occurring.
In summary, though the usage of magic strings look innocuous initially, the more we use them the more unwieldy and difficult to manage they become.
Typed Views
The deficiencies introduced by the usage of magic strings in our application can be removed – by and large – via the usage of strongly typed views and ViewModel pattern. Let’s first start with a strongly typed view since usage of it necessitates our usage of ViewModel pattern.
For our Index view since we need to display a list of Posts, here’s how we make it a strongly typed view via the VS.NET 2010.
- First put the cursor in our Index action method, right-click and choose Add View option.
- Choose strongly typed view option and select the model it will bind to. In our case we choose the Post model.
Our resulting strongly typed Index view.
Code Snippet- <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Post>>" %>
- <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
- <h2>
- Index</h2>
- <% foreach (var post in Model)
- {%>
- <p>
- <%= post.Body%>
- </p>
- <%} %>
- </asp:Content>
Our code is more concise as we do not have to use casting. Similarly for our controller we we need to send our strongly typed variable for our view to use. Here’s how we modify our controller action method to make use of our strongly typed Index view.
Code Snippet- public class PostController : Controller
- {
- private readonly SimpleBlogDb _simpleBlogDb = new SimpleBlogDb();
- public ActionResult Index()
- {
- // Get the top 10 most recent posts.
- var posts = from d in _simpleBlogDb.Posts
- orderby d.CreatedOn ascending
- select d;
- ViewData["PopularPosts"] = _simpleBlogDb.PopularPosts;
- return View(posts.Take(10));
- }
- }
Note above where we send posts variable for the perusal of our view. Now, we did manage to make our view strongly typed to
IEnumerableand remove the usage of “Posts” magic string. However, “PopularPosts” magic string still remain. Therein lies our problem, typed views in ASP.NET MVC can only be associated with one Type but our view – Index – also needs Popular Posts data to fulfil our requirements. So how do we solve this problem?
ViewModel Pattern
Enter ViewModel pattern. As its name implies, the ViewModel pattern encapsulates the data requirements of a view. In ASP.NET MVC ViewModel is a POCO – plain old CLR object.
For our scenario, here’s how we implement it. The accepted convention in ASP.NET MVC is to put our view models in a separate folder named ‘ViewModels’.
And here’s our POCO ViewModel implementation.
- public class PostIndexViewModel
- {
- public IEnumerable<Post> Posts { get; set; }
- public IEnumerable<Post> PopularPosts { get; set; }
- }
We might start to wonder the differences between our Model classes and our ViewModel classes. One important difference being that ViewModels are not directly persisted in the database/storage. Secondly, the data contained within ViewModels are optimized for a specific view usage and as such are generally not reused across multiple views. Third, ViewModels encapsulate our Model classes as we have done in our PostIndexViewModel.
Now that we have our shiny new PostIndexViewModel, here’s how we use it in our controller action method and our view. First, our refactored Index controller action method.
- public class PostController : Controller
- {
- private readonly SimpleBlogDb _simpleBlogDb = new SimpleBlogDb();
- public ActionResult Index()
- {
- var postIndexViewModel = new PostIndexViewModel();
- // Get the top 10 most recent posts.
- var posts = from d in _simpleBlogDb.Posts
- orderby d.CreatedOn ascending
- select d;
- postIndexViewModel.Posts = posts;
- // Get the top 10 most read posts.
- var popularPosts = from p in _simpleBlogDb.PopularPosts
- orderby p.ViewedCount descending
- select p;
- postIndexViewModel.PopularPosts = popularPosts;
- return View(postIndexViewModel);
- }
- }
We have completely obviated the need for “magic strings” in our controller action method. And here’s our refactored Index view.
- <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<PostIndexViewModel>" %>
- <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
- <h2>
- Index</h2>
- <% foreach (var post in Model.Posts){%>
- <p><%= post.Body %></p>
- <%}%>
- </asp:Content>
Now that we have completely removed the usage of “magic strings” from our application, we can leverage the C# compiler to compile our views along with our other C# files. By default, in an ASP.NET MVC project, views are not compiled so we need to explicitly enable this property in our project file. Let’s open our .csproj file in a notepad or some other text editor. Look for the MvcBuildViews property and set its value to true.
Benefits of ViewModel and Strongly Typed Views
The benefit of using this approach to application development in ASP.NET MVC is that our development process is more productive. This is so because of the following
- The C# compiler notifies us of any errors – such as typos – during compile time. This is important because errors are caught sooner rather than later as would have been the case with the usage of “magic strings”. Sooner because we do not have to go through the whole “F5-open up the browser-run the application” cycle to detect the errors. In some cases, errors in magic strings could go undetected for a long time.
- Our development work-flow is more agile since we can refactor our code base with confidence knowing that the compiler will alert us to any syntactic or naming issues via the build error messages.
- Additionally, we have the benefit of editor intellisense within the view templates.
klay September 15, 2010 at 4:11 am
good one
JoaoC November 13, 2010 at 9:34 pm
Thanks. Concise and clearly argued. Improved my understanding of MVC.
Phil Q November 30, 2010 at 2:05 am
Excellent post! I will use it in my work. Thank you.
pelumini March 10, 2011 at 12:13 pm
Hi Bikal, what of if i need a view that display data from both posts and popular posts e.g. a table of columns from the two tables, how do i pass that into my view and iterate over the model?
Bikal Gurung March 14, 2011 at 10:59 am
@Pelumini. Here is what I would do. First, define a viewmodel PostViewModel and defined two property members on the class. The code below.
public class PostViewModel Posts {get;set;} Posts {get;set;}
{
public ICollection
public ICollection
}
Then in your controller,
public ActionResult Posts()
{
// initialise the view model here. Populate the fields are required.
var posts = new PostViewMode();
return View(posts);
}
The view should be a typed view of PostViewModel.
Does this help ?
Workaholic September 12, 2011 at 5:41 am
Say you have a viewmodel for your Create Action where you have Post type as well as another type called Tag. And you want to display list of tags as checkboxlist so that when you create a post you can check which tag that post belongs to. How would you handle selected items from checkboxlist on creating a new post?