Meditation and Mindfulness for Software / IT Professionals. Conducted by Bipin Joshi in Thane. Read more...

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!




Bipin Joshi is a software consultant, an author and a yoga mentor having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 09 Nov 2015



Tags : ASP.NET Data Access MVC C#