Retrieving and displaying data with model binding and web forms

This tutorial series demonstrates basic aspects of using model binding with an ASP.NET Web Forms project. Model binding makes data interaction more straight-forward than dealing with data source objects (such as ObjectDataSource or SqlDataSource). This series starts with introductory material and moves to more advanced concepts in later tutorials.

The model binding pattern works with any data access technology. In this tutorial, you will use Entity Framework, but you could use the data access technology that is most familiar to you. From a data-bound server control, such as a GridView, ListView, DetailsView, or FormView control, you specify the names of the methods to use for selecting, updating, deleting, and creating data. In this tutorial, you will specify a value for the SelectMethod.

Within that method, you provide the logic for retrieving the data. In the next tutorial, you will set values for UpdateMethod, DeleteMethod and InsertMethod.

You can download the complete project in C# or Visual Basic. The downloadable code works with Visual Studio 2012 and later. It uses the Visual Studio 2012 template, which is slightly different than the Visual Studio 2017 template shown in this tutorial.

In the tutorial you run the application in Visual Studio. You can also deploy the application to a hosting provider and make it available over the internet. Microsoft offers free web hosting for up to 10 web sites in a
free Azure trial account. For information about how to deploy a Visual Studio web project to Azure App Service Web Apps, see the ASP.NET Web Deployment using Visual Studio series. That tutorial also shows how to use Entity Framework Code First Migrations to deploy your SQL Server database to Azure SQL Database.

Software versions used in the tutorial

  • Microsoft Visual Studio 2017 or Microsoft Visual Studio Community 2017

This tutorial also works with Visual Studio 2012 and Visual Studio 2013, but there are some differences in the user interface and project template.

What you'll build

In this tutorial, you'll:

  • Build data objects that reflect a university with students enrolled in courses
  • Build database tables from the objects
  • Populate the database with test data
  • Display data in a web form

Create the project

  1. In Visual Studio 2017, create a ASP.NET Web Application (.NET Framework) project called ContosoUniversityModelBinding.

    create project

  2. Select OK. The dialog box to select a template appears.

    select web forms

  3. Select the Web Forms template.

  4. If necessary, change the authentication to Individual User Accounts.

  5. Select OK to create the project.

Modify site appearance

Make a few changes to customize site appearance.

  1. Open the Site.Master file.

  2. Change the title to display Contoso University and not My ASP.NET Application.

    <title><%: Page.Title %> - Contoso University</title>
    
  3. Change the header text from Application name to Contoso University.

    <div class="navbar-header">
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" runat="server" href="~/">Contoso University</a>
    </div>
    
  4. Change the navigation header links to site appropriate ones.

    Remove the links for About and Contact and, instead, link to a Students page, which you will create.

    <ul class="nav navbar-nav">
        <li><a runat="server" href="~/">Home</a></li>
        <li><a runat="server" href="~/Students">Students</a></li>
    </ul>
    
  5. Save Site.Master.

Add a web form to display student data

  1. In Solution Explorer, right-click your project, select Add and then New Item.

  2. In the Add New Item dialog box, select the Web Form with Master Page template and name it Students.aspx.

    create page

  3. Select Add.

  4. For the web form's master page, select Site.Master.

  5. Select OK.

Add the data model

In the Models folder, add a class named UniversityModels.cs.

  1. Right-click Models, select Add, and then New Item. The Add New Item dialog box appears.

  2. From the left navigation menu, select Code, then Class.

    create model class

  3. Name the class UniversityModels.cs and select Add.

    In this file, define the SchoolContext, Student, Enrollment, and Course classes as follows:

    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.ComponentModel.DataAnnotations;
    
    namespace ContosoUniversityModelBinding.Models
    {
        public class SchoolContext : DbContext
        {
            public DbSet<Student> Students { get; set; }
            public DbSet<Enrollment> Enrollments { get; set; }
            public DbSet<Course> Courses { get; set; }
        }
    
        public class Student
        {
            [Key, Display(Name = "ID")]
            [ScaffoldColumn(false)]
            public int StudentID { get; set; }
    
            [Required, StringLength(40), Display(Name="Last Name")]
            public string LastName { get; set; }
    
            [Required, StringLength(20), Display(Name = "First Name")]
            public string FirstName { get; set; }
    
            [EnumDataType(typeof(AcademicYear)), Display(Name = "Academic Year")]
            public AcademicYear Year { get; set; }
    
            public virtual ICollection<Enrollment> Enrollments { get; set; }
        }
    
        public class Enrollment
        {
            [Key]
            public int EnrollmentID { get; set; }
            public int CourseID { get; set; }
            public int StudentID { get; set; }
            public decimal? Grade { get; set; }
            public virtual Course Course { get; set; }
            public virtual Student Student { get; set; }
        }
    
        public class Course
        {
            [Key]
            public int CourseID { get; set; }
            public string Title { get; set; }
            public int Credits { get; set; }
            public virtual ICollection<Enrollment> Enrollments { get; set; }
        } 
    
        public enum AcademicYear
        {
            Freshman,
            Sophomore,
            Junior,
            Senior
        }
    }
    

    The SchoolContext class derives from DbContext, which manages the database connection and changes in the data.

    In the Student class, notice the attributes applied to the FirstName, LastName, and Year properties. This tutorial uses these attributes for data validation. To simplify the code, only these properties are marked with data-validation attributes. In a real project, you would apply validation attributes to all properties needing validation.

  4. Save UniversityModels.cs.

Set up the database based on classes

This tutorial uses Code First Migrations to create objects and database tables. These tables store information about the students and their courses.

  1. Select Tools > NuGet Package Manager > Package Manager Console.

  2. In Package Manager Console, run this command:
    enable-migrations -ContextTypeName ContosoUniversityModelBinding.Models.SchoolContext

    If the command completes successfully, a message stating migrations have been enabled appears.

    enable migrations

    Notice that a file named Configuration.cs has been created. The Configuration class has a Seed method, which can pre-populate the database tables with test data.

Pre-populate the database

  1. Open Configuration.cs.

  2. Add the following code to the Seed method. Also, add a using statement for the ContosoUniversityModelBinding. Models namespace.

    namespace ContosoUniversityModelBinding.Migrations
    {
        using System;
        using System.Data.Entity;
        using System.Data.Entity.Migrations;
        using System.Linq;
        using ContosoUniversityModelBinding.Models;
    
        internal sealed class Configuration : DbMigrationsConfiguration<SchoolContext>
        {
            public Configuration()
            {
                AutomaticMigrationsEnabled = false;
            }
    
            protected override void Seed(SchoolContext context)
            {
    
                context.Students.AddOrUpdate(
                     new Student { 
                         FirstName = "Carson", 
                         LastName = "Alexander", 
                         Year = AcademicYear.Freshman },
                     new Student { 
                         FirstName = "Meredith", 
                         LastName = "Alonso", 
                         Year = AcademicYear.Freshman },
                     new Student { 
                         FirstName = "Arturo", 
                         LastName = "Anand", 
                         Year = AcademicYear.Sophomore },
                     new Student { 
                         FirstName = "Gytis", 
                         LastName = "Barzdukas", 
                         Year = AcademicYear.Sophomore },
                     new Student { 
                         FirstName = "Yan", 
                         LastName = "Li", 
                         Year = AcademicYear.Junior },
                     new Student { 
                         FirstName = "Peggy", 
                         LastName = "Justice", 
                         Year = AcademicYear.Junior },
                     new Student { 
                         FirstName = "Laura", 
                         LastName = "Norman", 
                         Year = AcademicYear.Senior },
                     new Student { 
                         FirstName = "Nino", 
                         LastName = "Olivetto", 
                         Year = AcademicYear.Senior }
                     );
    
                context.SaveChanges();
    
                context.Courses.AddOrUpdate(
                    new Course { Title = "Chemistry", Credits = 3 },
                    new Course { Title = "Microeconomics", Credits = 3 },
                    new Course { Title = "Macroeconomics", Credits = 3 },
                    new Course { Title = "Calculus", Credits = 4 },
                    new Course { Title = "Trigonometry", Credits = 4 },
                    new Course { Title = "Composition", Credits = 3 },
                    new Course { Title = "Literature", Credits = 4 }
                    );
    
                context.SaveChanges();
    
                context.Enrollments.AddOrUpdate(
                    new Enrollment { StudentID = 1, CourseID = 1, Grade = 1 },
                    new Enrollment { StudentID = 1, CourseID = 2, Grade = 3 },
                    new Enrollment { StudentID = 1, CourseID = 3, Grade = 1 },
                    new Enrollment { StudentID = 2, CourseID = 4, Grade = 2 },
                    new Enrollment { StudentID = 2, CourseID = 5, Grade = 4 },
                    new Enrollment { StudentID = 2, CourseID = 6, Grade = 4 },
                    new Enrollment { StudentID = 3, CourseID = 1 },
                    new Enrollment { StudentID = 4, CourseID = 1 },
                    new Enrollment { StudentID = 4, CourseID = 2, Grade = 4 },
                    new Enrollment { StudentID = 5, CourseID = 3, Grade = 3 },
                    new Enrollment { StudentID = 6, CourseID = 4 },
                    new Enrollment { StudentID = 7, CourseID = 5, Grade = 2 }
                    );
    
                context.SaveChanges();
            }
        }
    }
    
  3. Save Configuration.cs.

  4. In the Package Manager Console, run the command add-migration initial.

  5. Run the command update-database.

    If you receive an exception when running this command, the StudentID and CourseID values might be different from the Seed method values. Open those database tables and find existing values for StudentID and CourseID. Add those values to the code for seeding the Enrollments table.

Add a GridView control

With populated database data, you're now ready to retrieve that data and display it.

  1. Open Students.aspx.

  2. Locate the MainContent placeholder. Within that placeholder, add a GridView control that includes this code.

    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
        <asp:GridView runat="server" ID="studentsGrid"
            ItemType="ContosoUniversityModelBinding.Models.Student" DataKeyNames="StudentID" 
            SelectMethod="studentsGrid_GetData"
            AutoGenerateColumns="false">
            <Columns>
                <asp:DynamicField DataField="StudentID" />
                <asp:DynamicField DataField="LastName" />
                <asp:DynamicField DataField="FirstName" />
                <asp:DynamicField DataField="Year" />          
                <asp:TemplateField HeaderText="Total Credits">
                  <ItemTemplate>
                    <asp:Label Text="<%# Item.Enrollments.Sum(en => en.Course.Credits) %>" 
                        runat="server" />
                  </ItemTemplate>
                </asp:TemplateField>        
            </Columns>
        </asp:GridView>
    </asp:Content>
    

    Things to note:

    • Notice the value set for the SelectMethod property in the GridView element. This value specifies the method used to retrieve GridView data, which you create in the next step.

    • The ItemType property is set to the Student class created earlier. This setting allows you to reference class properties in the markup. For example, the Student class has a collection named Enrollments. You can use Item.Enrollments to retrieve that collection and then use LINQ syntax to retrieve each student's enrolled credits sum.

  3. Save Students.aspx.

Add code to retrieve data

In the Students.aspx code-behind file, add the method specified for the SelectMethod value.

  1. Open Students.aspx.cs.

  2. Add using statements for the ContosoUniversityModelBinding. Models and System.Data.Entity namespaces.

    using ContosoUniversityModelBinding.Models;
    using System.Data.Entity;
    
  3. Add the method you specified for SelectMethod:

    public IQueryable<Student> studentsGrid_GetData()
    {
        SchoolContext db = new SchoolContext();
        var query = db.Students.Include(s => s.Enrollments.Select(e => e.Course));
        return query;
    }
    

    The Include clause improves query performance but isn't required. Without the Include clause, the data is retrieved using lazy loading, which involves sending a separate query to the database each time related data is retrieved. With the Include clause, data is retrieved using eager loading, which means a single database query retrieves all related data. If related data isn't used, eager loading is less efficient because more data is retrieved. However, in this case, eager loading gives you the best performance because the related data is displayed for each record.

    For more information about performance considerations when loading related data, see the Lazy, Eager, and Explicit Loading of Related Data section in the Reading Related Data with the Entity Framework in an ASP.NET MVC Application article.

    By default, the data is sorted by the values of the property marked as the key. You can add an OrderBy clause to specify a different sort value. In this example, the default StudentID property is used for sorting. In the Sorting, Paging, and Filtering Data article, the user is enabled to select a column for sorting.

  4. Save Students.aspx.cs.

Run your application

Run your web application (F5) and navigate to the Students page, which displays the following:

show data

Automatic generation of model binding methods

When working through this tutorial series, you can simply copy the code from the tutorial to your project. However, one disadvantage of this approach is that you may not become aware of the feature provided by Visual Studio to automatically generate code for model binding methods. When working on your own projects, automatic code generation can save you time and help you gain a sense of how to implement an operation. This section describes the automatic code generation feature. This section is only informational and does not contain any code you need to implement in your project.

When setting a value for the SelectMethod, UpdateMethod, InsertMethod, or DeleteMethod properties in the markup code, you can select the Create New Method option.

create a method

Visual Studio not only creates a method in the code-behind with the proper signature, but also generates implementation code to perform the operation. If you first set the ItemType property before using the automatic code generation feature, the generated code uses that type for the operations. For example, when setting the UpdateMethod property, the following code is automatically generated:

// The id parameter name should match the DataKeyNames value set on the control
public void studentsGrid_UpdateItem(int id)
{
    ContosoUniversityModelBinding.Models.Student item = null;
    // Load the item here, e.g. item = MyDataLayer.Find(id);
    if (item == null)
    {
        // The item wasn't found
        ModelState.AddModelError("", String.Format("Item with id {0} was not found", id));
        return;
    }
    TryUpdateModel(item);
    if (ModelState.IsValid)
    {
        // Save changes here, e.g. MyDataLayer.SaveChanges();

    }
}

Again, this code doesn't need to be added to your project. In the next tutorial, you'll implement methods for updating, deleting, and adding new data.

Summary

In this tutorial, you created data model classes and generated a database from those classes. You filled the database tables with test data. You used model binding to retrieve data from the database, and then displayed the data in a GridView.

In the next tutorial in this series, you'll enable updating, deleting, and creating data.