Passing a Model to a View

An action hands its view a model with View(model); the view names the model's type with @model and reads it through Model. ViewData carries small extras, such as the page title.

Journeyman · ASP.NET Core · Razor · C# · Updated

After this lesson you can

  • Pass a model from an action to its view with View(model)
  • Declare a view's model type with @model and read it through Model
  • Pass a small extra value to a view with ViewData

Key takeaways

  • return View(model) runs the view named after the action, in the folder named after the controller, such as Views/Products/Index.cshtml.
  • @model at the top of a view names the model's type; Model is the object the action passed.
  • Because the view knows its model's type, a misspelled property is a build error, not a blank page.
  • Use the model for the page's real data, and ViewData only for small extras, such as the page's title.

A view is a .cshtml file: HTML with C# mixed in, written in a syntax called Razor. The controller decides what to show; the view decides how it looks.

Which view runs

return View(...) looks for a view with the action's name, in a folder with the controller's name:

Controller and action View file
ProductsController.Index Views/Products/Index.cshtml
ProductsController.Details Views/Products/Details.cshtml
HomeController.Privacy Views/Home/Privacy.cshtml

If it isn't there, ASP.NET Core looks in Views/Shared next. (In Visual Studio, right-click inside an action and choose Add View... to make the file in the right place.)

The model: one object from the action to the view

In the action, pass the data to View:

// GET /Products
public IActionResult Index()
{
    List<Product> products = productDao.GetAll();
    return View(products);
}

At the top of the view, say what type the model is with @model, then use it through the Model property:

@model List<Product>
@{
    ViewData["Title"] = "Products";
}

<h1>Products</h1>
<p>Northwind sells @Model.Count products.</p>

<table class="table table-striped">
    <thead>
        <tr>
            <th>Product</th>
            <th class="text-end">Price</th>
            <th class="text-end">In stock</th>
        </tr>
    </thead>
    <tbody>
        @foreach (Product product in Model)
        {
            <tr>
                <td>@product.ProductName</td>
                <td class="text-end">@product.UnitPrice?.ToString("C")</td>
                <td class="text-end">@product.UnitsInStock</td>
            </tr>
        }
    </tbody>
</table>

Save it as Views/Products/Index.cshtml and go to /Products. Razor runs the C# and sends plain HTML; the first rows look like this:

<h1>Products</h1>
<p>Northwind sells 77 products.</p>
...
<tr>
    <td>Alice Mutton</td>
    <td class="text-end">$39.00</td>
    <td class="text-end">0</td>
</tr>
<tr>
    <td>Aniseed Syrup</td>
    <td class="text-end">$10.00</td>
    <td class="text-end">13</td>
</tr>

Two different words, and both matter:

  • @model (small m) goes once, at the top. It tells Razor the model's type.
  • Model (capital M) is the model itself, the object the action passed.

Because the view knows the type, Visual Studio offers Model.Count, product.ProductName and the rest as you type, and a misspelled property is a build error, not a blank page.

Product needs no using in the view: the project's Views/_ViewImports.cshtml already has @using NorthwindWeb.Models for every view.

One product

The details page gets one Product:

@model Product
@{
    ViewData["Title"] = Model.ProductName;
}

<h1>@Model.ProductName</h1>

<p>Price: @Model.UnitPrice?.ToString("C")</p>
<p>In stock: @Model.UnitsInStock</p>

Save it as Views/Products/Details.cshtml, and go to /Products/Details/7 for Uncle Bob's Organic Dried Pears, $30.00.

ViewData: small extras

A view has one model. For a small extra value, such as the text of a search box or the page's title, there's ViewData: a dictionary that the action and the view share for one request.

ViewData["SearchText"] = name;
@{
    string? searchText = ViewData["SearchText"] as string;
}
<p>You searched for "@searchText".</p>

ViewData holds object values, so the view has to convert them back to their type (as string), and a misspelled key gives null instead of a build error. Use the model for the page's real data, and ViewData only for extras. The views above already use it for one: ViewData["Title"] sets the browser tab's title, as you'll see in Layouts and _ViewStart.

Common problems

What you see What to do
The model item passed into the ViewDataDictionary is of type '...', but this ViewDataDictionary instance requires a model item of type '...' The action passed one type and @model names another. Make them match.
NullReferenceException on Model The action returned View() without the model. Pass it: View(products).
The type or namespace name 'Product' could not be found The model's namespace isn't in _ViewImports.cshtml. Add @using NorthwindWeb.Models.

Check yourself

3 questions to try on the lesson page.

So what

View(model) in the action, @model and Model in the view: that handshake is how every page gets its data. The controller fetches, the view shows, and the model is what passes between them.

Key terms

  • .cshtml File
  • @model
  • Strongly Typed View
  • ViewData

Cheat sheets