Strongly Typed Views
A strongly typed view knows exactly what data it receives. The compiler checks your property names, the editor autocompletes them, and refactoring stops breaking pages silently.
On this page · 6 min · Intermediate
On this page
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 6 min
- Difficulty
- Intermediate
- Updated
- Oct 29, 2026
Related tutorials
Learning objectives#
- Declare a view’s model type with @model
- Design a view model for one page
- Use DisplayFor and DisplayNameFor with [Display] and [DisplayFormat]
- Explain why view models are better than entities and ViewBag
Explanation#
@model OrderSummaryViewModel at the top of a view makes the Model property that type. Model.Total is checked when the view compiles; Model.Totl is an error, not a silent blank.
A view model is a class made for one view: it contains exactly what the page shows, already shaped (CustomerName instead of Customer.FirstName + LastName). The controller builds it from entities.
Lambda-based helpers read metadata from the model: Html.DisplayNameFor(m => m.PlacedOn) shows the [Display(Name = "Placed on")] label, and Html.DisplayFor applies [DisplayFormat(DataFormatString = "{0:dd MMM yyyy}")].
View models protect you: the view cannot accidentally show or post fields you did not include (like IsAdmin or PasswordHash), and changes to the database schema do not ripple into every view.
Syntax#
public class OrderSummaryViewModel
{
[Display(Name = "Order number")] public int Id { get; set; }
[DisplayFormat(DataFormatString = "{0:N2}")] public decimal Total { get; set; }
}
@model OrderSummaryViewModel
@Html.DisplayNameFor(m => m.Total): @Html.DisplayFor(m => m.Total)
Code example: An order summary page with a view model#
// Models/OrderSummaryViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace ShopSite.Models
{
public class OrderSummaryViewModel
{
[Display(Name = "Order number")]
public int Id { get; set; }
[Display(Name = "Customer")]
public string CustomerName { get; set; }
[Display(Name = "Placed on")]
[DisplayFormat(DataFormatString = "{0:dd MMM yyyy}")]
public DateTime PlacedOn { get; set; }
[DisplayFormat(DataFormatString = "{0:N2}")]
public decimal Total { get; set; }
public List<string> Items { get; set; }
}
}
// Controllers/OrdersController.cs
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using ShopSite.Models;
namespace ShopSite.Controllers
{
public class OrdersController : Controller
{
public ActionResult Summary(int id)
{
// In a real app this is mapped from the Order and Customer entities
var model = new OrderSummaryViewModel
{
Id = id,
CustomerName = "Asha Patil",
PlacedOn = new DateTime(2026, 10, 27),
Total = 1234.5m,
Items = new List<string> { "Gel Pen x3", "Notebook x2" },
};
return View(model);
}
}
}
@* Views/Orders/Summary.cshtml *@
@model OrderSummaryViewModel
<h1>@Html.DisplayNameFor(m => m.Id) @Model.Id</h1>
<dl>
<dt>@Html.DisplayNameFor(m => m.CustomerName)</dt>
<dd>@Html.DisplayFor(m => m.CustomerName)</dd>
<dt>@Html.DisplayNameFor(m => m.PlacedOn)</dt>
<dd>@Html.DisplayFor(m => m.PlacedOn)</dd>
<dt>@Html.DisplayNameFor(m => m.Total)</dt>
<dd>@Html.DisplayFor(m => m.Total)</dd>
</dl>
<p>@Model.Items.Count items: @string.Join(", ", Model.Items)</p>
GET /Orders/Summary/5001
200 OK
<h1>Order number 5001</h1>
<dl>
<dt>Customer</dt>
<dd>Asha Patil</dd>
<dt>Placed on</dt>
<dd>27 Oct 2026</dd>
<dt>Total</dt>
<dd>1,234.50</dd>
</dl>
<p>2 items: Gel Pen x3, Notebook x2</p>This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- @model OrderSummaryViewModel gives the view a typed Model and lambda helpers.
- DisplayNameFor reads [Display(Name)]; Total has no Display attribute, so its property name is used.
- DisplayFor applies [DisplayFormat]: the date becomes 27 Oct 2026 and the total 1,234.50.
- The view model already has CustomerName as one string, so the view does not need to know how names are stored.
Real-world use case#
A bank’s statement page shows formatted dates and amounts using DisplayFor with [DisplayFormat] on the view model. When the format changes to show currency codes, only the attribute changes — every view using DisplayFor updates.
Common mistakes#
Entities often contain fields you must not show or accept, and lazy-loaded navigation properties can trigger database queries while the view renders. Map to a view model.
Passing a List<Product> to a view declared @model Product throws “The model item passed into the dictionary is of type…”. Check what the action passes.
ViewBag.Products works until someone renames it in one place. A typed model fails at compile time instead.
Best practices#
- One view model per page (or per form)
- Keep formatting in [DisplayFormat] and DisplayFor
- Map entities to view models in the controller or a mapper
- Turn on MvcBuildViews in release builds to catch view errors at compile time
Remember this#
@model = typed Model + IntelliSense.
View model = exactly what the page needs.
DisplayNameFor/DisplayFor read [Display]/[DisplayFormat].
Practice exercise#
Your task: Create a ProfileViewModel with FullName, JoinedOn (format yyyy-MM-dd) and Points (format N0, label “Reward points”) and render it with DisplayNameFor and DisplayFor.
Need a hint?
Show solution
// Models/ProfileViewModel.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace ShopSite.Models
{
public class ProfileViewModel
{
[Display(Name = "Name")]
public string FullName { get; set; }
[Display(Name = "Member since")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime JoinedOn { get; set; }
[Display(Name = "Reward points")]
[DisplayFormat(DataFormatString = "{0:N0}")]
public int Points { get; set; }
}
}
// Controllers/ProfileController.cs
using System;
using System.Web.Mvc;
using ShopSite.Models;
namespace ShopSite.Controllers
{
public class ProfileController : Controller
{
public ActionResult Index() => View(new ProfileViewModel { FullName = "Ravi Kumar", JoinedOn = new DateTime(2024, 1, 15), Points = 12500 });
}
}
@* Views/Profile/Index.cshtml *@
@model ProfileViewModel
<p>@Html.DisplayNameFor(m => m.FullName): @Html.DisplayFor(m => m.FullName)</p>
<p>@Html.DisplayNameFor(m => m.JoinedOn): @Html.DisplayFor(m => m.JoinedOn)</p>
<p>@Html.DisplayNameFor(m => m.Points): @Html.DisplayFor(m => m.Points)</p>
GET /Profile 200 OK <p>Name: Ravi Kumar</p> <p>Member since: 2024-01-15</p> <p>Reward points: 12,500</p>
Mini quiz#
Which directive makes a view strongly typed?
Want more? Practise all ASP.NET MVC MCQs · ASP.NET MVC interview questions
My notes#
Saved in this browser.