Your First MVC 6 and EF 7 Application (Form using Tag Helpers) : Part 2
In Part 1 of this
series you created and configured a basic project using ASP.NET MVC 6. Although
the project runs as expected and outputs a message in the browser, it isn't
database driven yet. In this part we will add database support to the project in
the following ways:
- Display a list of customers from the Northwind database
- Allow for modification of existing customers
- Provide basic validation capabilities
In this part we will modify the Index view to look like this:

As you can see, the page displays a list of customers from the Customers
table of the Northwind database. Each table row has an Edit link. Clicking on
the Edit link takes you to the Edit view so that the customer details can be
modified. (see below).

Once you modify the details and click on the Save button the modified details
are saved in the database. In case there are any validation errors they are
displayed on the page like this:

Ok. Let's begin our development!
Open the same project that we created in
Part 1 and add Customer
class to the Models folder using the Add New Items dialog. The complete code of
this class is shown below:
[Table("Customers")]
public class Customer
{
[Required]
[StringLength(5)]
public string CustomerID { get; set; }
[Required]
public string CompanyName { get; set; }
[Required]
public string ContactName { get; set; }
[Required]
public string Country { get; set; }
}
The Customer class is mapped to the Customers table using the [Table]
attribute and contains four public properties namely CustomerID, CompanyName,
ContactName and Country. Basic validations such as [Required] and [StringLength]
are also added to these properties.
Then add NorthwindDbContext class to the Classes folder and write the
following code to it.
public class NorthwindDbContext:DbContext
{
public DbSet<Customer> Customers { get; set; }
protected override void OnConfiguring
(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(AppSettings.ConnectionString);
}
}
The NorthwindDbContext class represents our data context and hence inherits
from the DbContext base class. It consists of a single DbSet - Customers. Notice
how the OnConfiguring() method has been overridden to specify the database
connection string.
The overridden OnConfiguring() method supplies optionsBuilder parameter. The
UseSqlServer() method accepts a database connection string. Recollect that we
have stored the database connection string in the ConnectionString static
property of the AppSettings class during startup of the application.
Now modify the Index() action of the HomeController as shown below:
public IActionResult Index()
{
using (NorthwindDbContext db = new NorthwindDbContext())
{
List<Customer> data = db.Customers.ToList();
return View(data);
}
}
The Index() action simply instantiates the NorthwindDbContext and fetches all
the customers in the form of a List. This List is supplied to the Index view as
its model.
Now, open the Index view and modify it as shown below:
@model List<MVC6Demo.Models.Customer>
<html>
<head>
<title>My First MVC 6 Application</title>
</head>
<body>
<h1>List of Customers</h1>
<table border="1" cellpadding="10">
@foreach (var item in Model)
{
<tr>
<td>@item.CustomerID</td>
<td>@item.CompanyName</td>
<td><a asp-action="Edit"
asp-controller="Home"
asp-route-id="@item.CustomerID">
Edit</a></td>
</tr>
}
</table>
</body>
</html>
The Index view displays a list of customers in a table. This code is quite
similar to MVC 5.x except the Edit link. In MVC 5.x you use ActionLink() HTML
helper to render hyperlinks. The above code uses an anchor Tag Helper to achieve
the same. The asp-action and asp-controller attributes points to the action
method and the controller. The asp-route-id attribute specifies the ID route
parameter to a CustomerID. This way the Edit links will take this form:
/home/edit/ALFKI
To get the Tag Helper intellisense you need to add _ViewImports.cshtml file
to the Views folder (you can do that using Add New Items dialog). Once added
place the following code in it:
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
The @addTagHelper directive tells the framework to use Tag Helpers from the
specified assembly. You will now get various tag helper related attributes in
the Visual Studio intellisense.
Ok. Next, add Edit() action to the HomeController as shown below:
public IActionResult Edit(string id)
{
using (NorthwindDbContext db = new NorthwindDbContext())
{
Customer data = db.Customers.Where(i =>
i.CustomerID == id).SingleOrDefault();
var query = (from c in db.Customers
orderby c.Country ascending
select new SelectListItem()
{ Text = c.Country, Value = c.Country })
.Distinct();
List<SelectListItem> countries = query.ToList();
ViewBag.Countries = countries;
return View(data);
}
}
The Edit action receives a CustomerID as its parameter. Inside, the code
fetches a Customer matching the supplied ID and passes it to the Edit view. The
Edit view also needs a list of countries for the Country column. So, a List of
SelectListItem (Microsoft.AspNet.Mvc.Rendering namespace) is created and filled
with unique countries from the Customers table. This List is passed to the view
through the Countries ViewBag property.
Then add Edit view under Views/Home folder and key-in the following markup in
it:
@model MVC6Demo.Models.Customer
<html>
<head>
<title>My First MVC 6 Application</title>
<style>
.field-validation-error
{
color:red;
}
.validation-summary-errors
{
color:red;
}
</style>
</head>
<body>
<h1>Edit Customer</h1>
<form asp-controller="Home" asp-action="Save" method="post">
<table border="1" cellpadding="10">
<tr>
<td>
<label asp-for="CustomerID">Customer ID :</label>
</td>
<td>
<input asp-for="CustomerID" readonly="readonly" />
<span asp-validation-for="CustomerID"></span>
</td>
</tr>
<tr>
<td>
<label asp-for="CompanyName">Company Name :</label>
</td>
<td>
<input asp-for="CompanyName" />
<span asp-validation-for="CompanyName"></span>
</td>
</tr>
<tr>
<td>
<label asp-for="ContactName">Contact Name :</label>
</td>
<td>
<input asp-for="ContactName" />
<span asp-validation-for="ContactName"></span>
</td>
</tr>
<tr>
<td>
<label asp-for="Country">Country :</label>
</td>
<td>
<select asp-for="Country"
asp-items="@ViewBag.Countries"></select>
<span asp-validation-for="Country"></span>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Save" />
</td>
</tr>
</table>
</form>
<div asp-validation-summary="ValidationSummary.All"></div>
<br />
<a asp-action="Index" asp-controller="Home">Go Back</a>
</body>
</html>
The above markup uses the following Tag Helpers:
- form
- label
- input
- select
- field validation and validation summary
The asp-action and asp-controller attributes of the form tag helper are set
to Save and Home respectively. This way the form will be POSTed to the Save()
action of the HomeController. The asp-for attribute of the label and the input
tag helpers specify a model property that is bound with the label and the input
field respectively. The asp-items attribute of the select tag helper specify
that the <option> elements be generated from the Countries ViewBag property.
The field level validations are displayed by adding <span> elements and
setting their asp-validation-for attribute to the appropriate model property.
This way the model validation errors will be displayed in the <span> elements.
The validation summary is displayed in a <div> element by setting its
asp-validation-summary attribute to ValidationSummary.All. The look and feel of
the validation tag helpers is controlled through CSS classes -
.field-validation-error and .validation-summary-errors (see top of the markup).
Now add Save() action to the HomeController and write the following code to
it:
public IActionResult Save(Customer obj)
{
using (NorthwindDbContext db =
new NorthwindDbContext())
{
var query = (from c in db.Customers
orderby c.Country ascending
select new SelectListItem()
{ Text = c.Country, Value = c.Country }).Distinct();
List<SelectListItem> countries = query.ToList();
ViewBag.Countries = countries;
if (ModelState.IsValid)
{
db.Entry(obj).State = EntityState.Modified;
db.SaveChanges();
}
return View("Edit", obj);
}
}
The code that fills the Countries ViewBag property is same as before. Then
the code checks whether the ModelState is valid using the IsValid property. If
the model state is valid the modified Customer details are saved to the
database. This is done by setting the State property to Modified and then
calling SaveChanges() method.
That's it! Run the application and check if the customer details can be
modified successfully.
In this part you instantiated NorthwindDbContext locally. In the next part we
will use MVC 6 dependency injection to inject it into the HomeController. Till
then keep coding!