In the last module we seeded the database with some test data so we would have some existing vehicles ready to start developing the features of our application with.
In this module we are going to develop our first feature and display a list of vehicles to the user. Then we will wire up the category buttons so that a user can search the results by vehicle type. Are you excited yet?
Return a Model from the controller
Modify the home controller with code below.
FredsCars/Controllers/HomeController.cs
using FredsCars.Data;
using Microsoft.AspNetCore.Mvc;
namespace FredsCars.Controllers
{
public class HomeController : Controller
{
private FredsCarsDbContext _context;
public HomeController(FredsCarsDbContext context)
{
_context = context;
}
public ViewResult Index()
{
return View(_context.Vehicles.ToList());
}
}
}
In the code above we’ve made only a few but very important changes.
The DI (Dependency Injection) pattern
In order to access the vehicle data from the database, we need an instance of the FredsCarsDbContext
service. And luckily for us, we have already registered it in the DI Service Container. So, we can inject the service into the constructor of the controller class.
And that is what we did with the following lines of code.
private FredsCarsDbContext _context;
public HomeController(FredsCarsDbContext context)
{
_context = context;
}
In the code above we declare a private field called _context of type FredsCarsDbContext
.
private FredsCarsDbContext _context;
In the constructor of the controller class we have added a parameter called context also of type FredsCarsDbContext
. In the body of the constructor we assign the incoming FredsCarsDbContext
service instance, context, to the private _context variable so we can use the service object throughout the body of the controller.
public HomeController(FredsCarsDbContext context)
{
_context = context;
}
This is the DI pattern and we will see it used over and over again. As we will see in later modules, loosely coupling the service from the controller like this will allow us to more easily unit test the controller when the time comes.
Return the Model
Next, we return a list of vehicles (List<Vehicle
>) as a model to the Home Index view.
public ViewResult Index()
{
return View(_context.Vehicles.ToList());
}
In the code above we have replaced the parameter-less View method call:
return View()
with one that takes in an object to return to the view as a model:
return View(_context.Vehicles.ToList());
In C# this is called overloading a method.
Overloading methods in C#
Overloading a method in C# refers to the practice of defining multiple methods with the same name in a class but with different parameter lists. This allows you to call the same method name in different ways, depending on the number, types, or order of the parameters.
Overloaded methods must differ in at least one of the following:
- The number of parameters.
- The type of parameters.
- The order of parameters.
The View
method of a controller has four overloads. The one we are currently using is:View(object? model)
.
If you delete the parenthesis following the View method keyword in Visual Studio and then re-type it back in, IntelliSense will pop up and you can see the signature of the current overload we are using. And you can scroll through the other signatures with up and down arrow buttons.

Display the Results in the View
Modify the Index View file of the Home Controller with the code below.
/FredsCars/Views/Home/Index.cshtml
@using FredsCars.Models
@model IEnumerable<Vehicle>
@{
ViewData["Title"] = "Welcome";
}
<div class="container-fluid my-4 text-center">
<h1>Welcome to Fred's Cars!</h1>
Where you'll always find the right car, truck, or jeep.<br />
Thank you for visiting our site!
<div class="container-fluid mx-0 row"
style="margin-top: 20px;">
<!-- Categories -->
<div class="col-4 col-md-3 col-lg-2"
style="border-right: 2px solid black">
<div class="d-grid gap-2 button-grid">
<a asp-controller="Vehicles"
asp-action="Index"
class="btn btn-primary button">
<b>ALL</b></a>
<a asp-controller="Vehicles"
asp-action="Index"
class="btn btn-outline-primary button">
<b>CARS</b></a>
<a asp-controller="Vehicles"
asp-action="Index"
class="btn btn-outline-primary button">
<b>TRUCKS</b></a>
<a asp-controller="Vehicles"
asp-action="Index"
class="btn btn-outline-primary button">
<b>JEEPS</b>
</a>
</div>
</div>
<!-- Results -->
<div class="col">
<h3 class="bg-dark text-success">ALL Results</h3>
<table class="results table table-striped">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Status)
</th>
<th>
@Html.DisplayNameFor(model => model.Year)
</th>
<th>
@Html.DisplayNameFor(model => model.Make)
</th>
<th>
@Html.DisplayNameFor(model => model.Model)
</th>
<th>
@Html.DisplayNameFor(model => model.Color)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th>
@Html.DisplayNameFor(model => model.VIN)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
<td>
@Html.DisplayFor(modelItem => item.Year)
</td>
<td>
@Html.DisplayFor(modelItem => item.Make)
</td>
<td>
@Html.DisplayFor(modelItem => item.Model)
</td>
<td>
@Html.DisplayFor(modelItem => item.Color)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.VIN)
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
In the code above we use the C# model
keyword at the top of the file to declare the type of the data model the view expects to receive from the controller. In this case we declare the type of the model we will be working with to be an IEnumerable of Vehicle (IEnumerable<Vehicle>
)
@using FredsCars.Models
@model IEnumerable<Vehicle>
NOTE: We passed a List<Vehicle>
from the controller but we declare the model in the View as an IEnumerable<Vehicle>
. We can do this because a List<T>
implements an IEnumerable<T>
and therefore is an IEnumerable
.
The Razor View model
As already noted the @model
keyword in a Razor file is used to declare the type of the data model that the view expects. This allows the view to strongly type its content, making it easier to work with the model’s properties and providing compile-time checking. By using a strongly typed model we also get Intellisense for the properties and methods of each Vehicle in the list.

Once declared, the @model
keyword makes the specified model available in the view as the Model
property. You can then use this property to access the data passed from the controller to the view.
// Model is an IEnumerable<Vehicle>
@foreach (var item in Model)
{
// lay out results
// item is a Vehicle
@Html.DisplayFor(modelItem => item.Status)
}
The next modification in the View file sets up a table where we can iterate through the List of Vehicles and lay out the results for the user to view.
Within the table element we have a thead section and a tbody section.
<table class="results table table-striped">
<thead>
// lay out header columns
</thead>
<tbody>
// lay out Vehicle result records
// display the Vehicle's properties as column data
</tbody>
<table>
Within the thead
element we have a tr
element to set up a table row. And within the tr
element we have th
elements that contain the table’s column headers. The th
element renders its data in bold to show it is a header rather than data.
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Status)
</th>
<th>
@Html.DisplayNameFor(model => model.Year)
</th>
... rest of the properties ...
</thead>
The code above uses the DisplayNameFor
property of the HtmlHelper
class accessed by the razor @Html object to display the name of the specified vehicle property as the column header text.
Within the tbody
element we have a foreach
block which loops through all of the Vehicles in the Model and lays out each Vehicle as a data row.
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
<td>
@Html.DisplayFor(modelItem => item.Year)
</td>
... rest of the data rows ...
}
</tbody>
The code above uses the DisplayFor
property of the HtmlHelper
class to display the value of the specified vehicle property as the column data.
That’s it. The existing code in the Home controller and Home Controller’s Index View should now display our Vehicle test data for us. Restart the application and point your browser to https://localhost:40443 and the results should look similar to the following.

Fix the table overflow with CSS
All looks well and good so far right? However there is a little problem. If I make the browser window smaller in height, the table data overflows the sticky footer.

We can fix this with a little styling for now. Add the code below to the bottom of site.css.
FredsCars/css/site.css
... existing code ...
/* Fix table overflow to
make search results scrollable and avoid
screwing up sticky footer.
*/
table.results tbody {
display: block;
max-height: 300px;
overflow-y: scroll;
}
table.results thead, table tbody tr {
display: table;
width: 90%;
table-layout: fixed;
}
}
Restart the application once again, point your browser to https://localhost:40443, and the results should look similar to the following.

Asynchronous programming in C#
The Index View in the Home controller currently uses synchronous programming.
public ViewResult Index()
{
return View(_context.Vehicles.ToList());
}
This is all well and good and seems to work fine for us in development. But, on a production server there could be hundreds, thousands, or even millions of consumers of the application. And, a server only has so many threads available to process requests.
If we use synchronous programming in these higher load scenarios, some of the processing threads could be tied up waiting for IO work to complete. They’ll just sit there doing nothing until the work is done. If all of the threads are tied up waiting for IO work to complete then a new request will have to wait for an available thread.
NOTE: IO work is processing done outside of our application we have to wait on, for instance, database calls.
We can use asynchronous programming to free up these threads while they are waiting on IO work to complete so that they can process other requests.
Let’s modify the Index method in the Home Controller to make it asynchronous. Modify the Home Controller with the code below.
... existing code ...
public HomeController(FredsCarsDbContext context)
{
_context = context;
}
public async Task<ViewResult> Index()
{
return View(await _context.Vehicles.ToListAsync());
}
}
}
Restart and run the application and it should behave the same. There will be no noticeable changes.
In the above code we have marked the method as asynchronous using the async
keyword and now instead of returning a ViewResult, we return a Task of ViewResult
or a Task<ViewResult>
.
Also the View method uses the await
keyword to wait for the database work to get a list of Vehicles to complete.
Finally, we swap out the ToList
method with the ToListAsync
method which returns a type of Task<List<Vehicle>>
.
Again, the Index View in the Home controller now returns a Task<ViewResult>
.
This is kind of like what’s known in JavaScript as a promise to return a ViewResult
once the IO work is done.
Import the Models Namespace to the View Imports file
Notice in the Index View file when we declared the model to be of type IEnumerable<Vehicle>
, we first had to import the FredsCars.Models
namespace with the razor @using
directive.
// top two lines in FredsCars/Views/Home/Index.cshtml
@using FredsCars.Models
@model IEnumerable<Vehicle>
Earlier in this chapter we created a View Imports file in the Views/Shared folder. We used it to register all of the built in tag helpers with the razor @addTagHelper
directive.
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
We can also use the View Imports file to import the FredsCars.Models
namespace (and any other namespaces we need) into all of our razor files. That way we don’t have to repeat the @using
directive for the same namespace in every razor file.
Cut the following statement from the top line of FredsCars/Views/Home.Index.cshtml and paste it into FredsCars/Views/ViewImports.cshtml.
@using FredsCars.Models
The FredsCars/Views/Home/Index.cshtml file should now look like the following.
@model IEnumerable<Vehicle>
@{
ViewData["Title"] = "Welcome";
}
... existing code ...
And the FredsCars/Views/_ViewImports.cshtml file should now look like the following.
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@using FredsCars.Models
If you restart the application and run it in the browser the results should remain the same.
Add the Price Migration
When we created the Vehicle entity class in our model, we made the Price property a value type of double.
public double Price { get; set; }
Based off of this, when Entity Framework Core created the database it mapped the C# datatype of double to an SQL data type of float.

There can be rounding issues common with floating-point types (float
or double
). Because of this you may have gotten this warning when we created the Initial Migration.
No store type was specified for the decimal property ‘Price’ on entity type ‘Movie’.
This will cause values to be silently truncated if they do not fit in the default precision and scale.
Explicitly specify the SQL server column type that can accommodate all the values in ‘OnModelCreating’ using ‘HasColumnType’, specify precision and scale using ‘HasPrecision’, or configure a value converter using ‘HasConversion’.
The C# decimal datatype represents money values more accurately then floating types. It’s high precision avoids the rounding issues mentioned above.
Use a Data Annotation to specify an SQL data type
We can use a data annotation to map the Price property to an appropriate SQL data type.
In C#, data annotations are attributes that provide a way to define metadata or validation rules for classes or properties in your application. They are commonly used in ASP.NET Core, Entity Framework, and other frameworks to enforce validation, define schema behavior, and influence how properties are mapped to database columns in Entity Framework.
Let’s change the data type of Price from double to decimal and apply the Column data type attribute with a TypeName property to map Price to the correct SQL data type.
Modify the Vehicle class with the code below.
FredsCars/Models/Vehicle.cs
using System.ComponentModel.DataAnnotations.Schema;
namespace FredsCars.Models
{
public enum Status
{
New,
Used
}
public class Vehicle
{
public int Id { get; set; }
public Status Status { get; set; }
public string Year { get; set; } = string.Empty;
public string Make { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
public string Color { get; set; } = string.Empty;
[Column(TypeName = "decimal(9, 2")]
public decimal Price { get; set; }
public string VIN { get; set; } = string.Empty;
// Foriegn Key to VehicleType entity/table row
public int VehicleTypeId { get; set; }
// Entity Framework Navigation Property
public VehicleType VehicleType { get; set; } = null!;
}
}
In the code above we imported the System.ComponentModel.DataAnnotations.Schema
namespace at the top of the file. This namespace allows us to use data annotations that affect the schema of the database.
using System.ComponentModel.DataAnnotations.Schema;
NOTE: The System.ComponentModel.DataAnnotations
namespace without Schema
has other data attributes such as for validation which we will see later.
Next we changed the datatype of Price from double to decimal and applied the data annotation attribute.
[Column(TypeName = "decimal(9, 2")]
public decimal Price { get; set; }
This annotation tells EF Core that the corresponding column in the database should be of type decimal
with a precision of 9 and a scale of 2
.
- Precision (9): The total number of digits allowed in the number, both to the left and right of the decimal point.
- Scale (
2
): The number of digits allowed to the right of the decimal point.
Create the migration
Open a console window pointed to the FredsCars project and run the following commands.
dotnet ef migrations add SetPriceDataType
dotnet ef database update
Now if we inspect the design of the Vehicle table in the database we can see that the C# Price property has been mapped to the SQL data type we specified.

Use a Data Annotation to render Price as Currency
Right now the Price field displays in a pretty bland manner. It would be nice to have a currency sign ($
) to the left of each price. We can accomplish this very easily with another data annotation.
Modify the Vehicles model with the following code.
FredsCars\Models\Vehicle.cs
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace FredsCars.Models
{
public enum Status
{
New,
Used
}
public class Vehicle
{
public int Id { get; set; }
public Status Status { get; set; }
public string Year { get; set; } = string.Empty;
public string Make { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
public string Color { get; set; } = string.Empty;
[Column(TypeName = "decimal(9, 2)")]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
public string VIN { get; set; } = string.Empty;
// Foriegn Key to VehicleType entity/table row
public int VehicleTypeId { get; set; }
// Entity Framework Navigation Property
public VehicleType VehicleType { get; set; } = null!;
}
}
If you restart and run the application, the Price field will now be formatted with the currency sign and commas in addition to the decimal point.

In the new Vehicle code we added a DataType as a second data annotation. Within the DataType attribute, we set the DataType enumeration property to Currency.
[Column(TypeName = "decimal(9, 2)")]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
public string VIN { get; set; } = string.Empty;
Notice at the top of the Vehicle file we also imported the System.ComponentModel.DataAnnotations
namespace in addition to the System.ComponentModel.DataAnnotations.Schema
namespace.
DataType
is in the DataAnnotations.Schema
namespace (without the .Schema tacked on the end). Rather than change the schema of the database, DataType
acts as a hint to the browser of how to display the field. So, we didn’t have to add a migration and update the database for the change to take affect.
We can use IntelliSense to see all of the different hints that are available from the DataType enumeration.

Use a navigation property to display the Category Type
Right now the VIN number of each vehicle is displayed in the last column of the table. That is probably a field the user will not be interested in when looking for a new vehicle. That is a detail we can save for the details page we will create in a later module.
Let’s replace the VIN column with a VehicleType column. Modify the HomeController with the code below.
FredsCars\Controllers\HomeController.cs
... existing code ...
public async Task<ViewResult> Index()
{
return View(await _context.Vehicles
.Include(v => v.VehicleType)
.ToListAsync());
}
... existing code ...
In the code above we use the LINQ related extension method, Include
, to include the VehicleType
for each Vehicle.
Remember the navigation property we set up in the Vehicle model?
// Foriegn Key to VehicleType entity/table row
public int VehicleTypeId { get; set; }
// Entity Framework Navigation Property
public VehicleType VehicleType { get; set; } = null!;
VehicleTypeId
is the foreign key that fills the VehicleType
navigation property.
The LINQ Include extension method relies on this navigation property to interpret the Lambda expression we passed to it.v => v.VehicleType
return View(await _context.Vehicles
.Include(v => v.VehicleType)
.ToListAsync());
The Include statement’s Lambda parameter can be thought of asv => v.VehicleType
translates to
(vehicle) goes into (vehicle.VehicleType)
.
Remember the Lambda is just a function that can be thought of as the following:
function (v)
{
return v.VehicleType
}
The parameter name ‘v’ for a vehicle is arbitrary and can be called anything. We could have called it ‘abc’ and C# would still know it is a Vehicle that is being passed into the function.
abc => abc.VehicleType

But do not actually make this change. Leave it as: v => v.VehicleType
.
At this point a List of Vehicles (List<Vehicle
), each Vehicle including it’s VehicleType, is being passed asynchronously to the View as the model.
Now modify the View with the code below.
FredsCars\Views\Home\Index.cshtml
... existing code ...
<!-- Results -->
<div class="col">
<h3 class="bg-dark text-success">ALL Results</h3>
<table class="results table table-striped">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Status)
</th>
<th>
@Html.DisplayNameFor(model => model.Year)
</th>
<th>
@Html.DisplayNameFor(model => model.Make)
</th>
<th>
@Html.DisplayNameFor(model => model.Model)
</th>
<th>
@Html.DisplayNameFor(model => model.Color)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th>
@Html.DisplayNameFor(model => model.VehicleType)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Status)
</td>
<td>
@Html.DisplayFor(modelItem => item.Year)
</td>
<td>
@Html.DisplayFor(modelItem => item.Make)
</td>
<td>
@Html.DisplayFor(modelItem => item.Model)
</td>
<td>
@Html.DisplayFor(modelItem => item.Color)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.VehicleType.Name)
</td>
</tr>
}
</tbody>
</table>
</div>
... existing code ...
In the code above we first made a change to the last column header to display the name of the VehicleType property of the Vehicle class rather then for the VIN property using the DisplayNameFor method of the HTML helper class.
<th>
@Html.DisplayNameFor(model => model.VehicleType)
</th>
Next, we changed the last data column to display the name property of the VehicleType property of the Vehicle object using the DisplayFor method of the Html helper class.
<td>
@Html.DisplayFor(modelItem => item.VehicleType.Name)
</td>
Without the Include method in the query in the Controller’s Index method including the VehicleTypes for the Vehicles, we would not be able to access the VehicleType.Name
property in order to display to the user. VehicleType would be null and we would get an exception.
Restart the application and your results should look similar to the following.

Use the display name attribute to specify a column name
In the section above we swapped out the VIN
column for the VehicleType
column. This will be much more useful to the user. But, the default column header name is based off the actual property name, VehicleType. This will probably look strange to the user. Let’s use another data annotation to specify what the VehicleType column name should be. Modify the VehicleType
class file with the code below.
FredsCars\Models\Vehicle.cs
... existing code ...
// Foriegn Key to VehicleType entity/table row
public int VehicleTypeId { get; set; }
// Entity Framework Navigation Property
[Display(Name = "Category")]
public VehicleType VehicleType { get; set; } = null!;
In the code above we are using the Name property of the Display data annotation attribute to specify the name that should render for the VehicleType
property of the Vehicle entity class.
Restart the application and your changes should look similar to the following.

Now the user can think of a VehicleType
as a Category. And once we get to paging and sorting, the user will be able to sort by “Category”.
What’s Next
In this module we developed a basic List feature for the application that displays a list of vehicles to the user.
We have talked a lot about DI (Dependency Injection) in building up to this module. Here we actually learned the DI pattern and got to see DI in action by injecting the DbContext
(the FredsCarsDbContext
service) into the Controller’s constructor. In the next module we will build on the DI pattern with another popular pattern called the repository pattern.
We also learned about Asynchronous programming and overloading methods in C#.
As stated in the next module instead of injecting the DbContext directly into the controller, we will build a repository. Then we will inject the DbContext into it, and inject the repository into the controller.