"
 
 
 
ASP.NET (snapshot 2017) Microsoft documentation and samples

Getting started with ASP.NET Core MVC and Entity Framework Core using Visual Studio (1 of 10)

By Tom Dykstra and Rick Anderson

A Razor Pages version of this tutorial is available (xref:)here. The Razor Pages version is easier to follow and covers more EF features. We recommend you follow the (xref:)Razor Pages version of this tutorial.

The Contoso University sample web application demonstrates how to create ASP.NET Core 2.0 MVC web applications using Entity Framework (EF) Core 2.0 and Visual Studio 2017.

The sample application is a web site for a fictional Contoso University. It includes functionality such as student admission, course creation, and instructor assignments. This is the first in a series of tutorials that explain how to build the Contoso University sample application from scratch.

Download or view the completed application.

EF Core 2.0 is the latest version of EF but does not yet have all the features of EF 6.x. For information about how to choose between EF 6.x and EF Core, see EF Core vs. EF6.x. If you choose EF 6.x, see the previous version of this tutorial series.

[!NOTE] * For the ASP.NET Core 1.1 version of this tutorial, see the VS 2017 Update 2 version of this tutorial in PDF format. * For the Visual Studio 2015 version of this tutorial, see the VS 2015 version of ASP.NET Core documentation in PDF format.

Prerequisites

[!INCLUDEinstall 2.0]

Troubleshooting

If you run into a problem you can’t resolve, you can generally find the solution by comparing your code to the completed project. For a list of common errors and how to solve them, see the Troubleshooting section of the last tutorial in the series. If you don’t find what you need there, you can post a question to StackOverflow.com for ASP.NET Core or EF Core.

[!TIP] This is a series of 10 tutorials, each of which builds on what is done in earlier tutorials. Consider saving a copy of the project after each successful tutorial completion. Then if you run into problems, you can start over from the previous tutorial instead of going back to the beginning of the whole series.

The Contoso University web application

The application you’ll be building in these tutorials is a simple university web site.

Users can view and update student, course, and instructor information. Here are a few of the screens you’ll create.

Students Index page
Students Index page
Students Edit page
Students Edit page

The UI style of this site has been kept close to what’s generated by the built-in templates, so that the tutorial can focus mainly on how to use the Entity Framework.

Create an ASP.NET Core MVC web application

Open Visual Studio and create a new ASP.NET Core C# web project named “ContosoUniversity”.

Set up the site style

A few simple changes will set up the site menu, layout, and home page.

Open *Views/Shared/_Layout.cshtml* and make the following changes:

The changes are highlighted.

[!code-cshtmlMain]

   1:  <!DOCTYPE html>
   2:  <html>
   3:  <head>
   4:      <meta charset="utf-8" />
   5:      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   6:      <title>@ViewData["Title"] - Contoso University</title>
   7:   
   8:      <environment names="Development">
   9:          <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
  10:          <link rel="stylesheet" href="~/css/site.css" />
  11:      </environment>
  12:      <environment names="Staging,Production">
  13:          <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
  14:                asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
  15:                asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
  16:          <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
  17:      </environment>
  18:      
  19:  </head>
  20:  <body>
  21:      <nav class="navbar navbar-inverse navbar-fixed-top">
  22:          <div class="container">
  23:              <div class="navbar-header">
  24:                  <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
  25:                      <span class="sr-only">Toggle navigation</span>
  26:                      <span class="icon-bar"></span>
  27:                      <span class="icon-bar"></span>
  28:                      <span class="icon-bar"></span>
  29:                  </button>
  30:                  <a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">Contoso University</a>
  31:              </div>
  32:              <div class="navbar-collapse collapse">
  33:                  <ul class="nav navbar-nav">
  34:                      <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
  35:                      <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
  36:                      <li><a asp-area="" asp-controller="Students" asp-action="Index">Students</a></li>
  37:                      <li><a asp-area="" asp-controller="Courses" asp-action="Index">Courses</a></li>
  38:                      <li><a asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a></li>
  39:                      <li><a asp-area="" asp-controller="Departments" asp-action="Index">Departments</a></li>
  40:                  </ul>
  41:              </div>
  42:          </div>
  43:      </nav>
  44:      <div class="container body-content">
  45:          @RenderBody()
  46:          <hr />
  47:          <footer>
  48:              <p>&copy; 2017 - Contoso University</p>
  49:          </footer>
  50:      </div>
  51:   
  52:      <environment names="Development">
  53:          <script src="~/lib/jquery/dist/jquery.js"></script>
  54:          <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
  55:          <script src="~/js/site.js" asp-append-version="true"></script>
  56:      </environment>
  57:      <environment names="Staging,Production">
  58:          <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
  59:                  asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
  60:                  asp-fallback-test="window.jQuery"
  61:                  crossorigin="anonymous"
  62:                  integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
  63:          </script>
  64:          <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
  65:                  asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
  66:                  asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
  67:                  crossorigin="anonymous"
  68:                  integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
  69:          </script>
  70:          <script src="~/js/site.min.js" asp-append-version="true"></script>
  71:      </environment>
  72:   
  73:      @RenderSection("Scripts", required: false)
  74:  </body>
  75:  </html>

In Views/Home/Index.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET and MVC with text about this application:

[!code-cshtmlMain]

   1:  @{
   2:      ViewData["Title"] = "Home Page";
   3:  }
   4:   
   5:  <div class="jumbotron">
   6:      <h1>Contoso University</h1>
   7:  </div>
   8:  <div class="row">
   9:      <div class="col-md-4">
  10:          <h2>Welcome to Contoso University</h2>
  11:          <p>
  12:              Contoso University is a sample application that
  13:              demonstrates how to use Entity Framework Core in an
  14:              ASP.NET Core MVC web application.
  15:          </p>
  16:      </div>
  17:      <div class="col-md-4">
  18:          <h2>Build it from scratch</h2>
  19:          <p>You can build the application by following the steps in a series of tutorials.</p>
  20:          <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
  21:      </div>
  22:      <div class="col-md-4">
  23:          <h2>Download it</h2>
  24:          <p>You can download the completed project from GitHub.</p>
  25:          <p><a class="btn btn-default" href="https://github.com/aspnet/Docs/tree/master/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code &raquo;</a></p>
  26:      </div>
  27:  </div>

Press CTRL+F5 to run the project or choose Debug > Start Without Debugging from the menu. You see the home page with tabs for the pages you’ll create in these tutorials.

Contoso University home page
Contoso University home page

Entity Framework Core NuGet packages

To add EF Core support to a project, install the database provider that you want to target. This tutorial uses SQL Server, and the provider package is Microsoft.EntityFrameworkCore.SqlServer. This package is included in the (xref:)Microsoft.AspNetCore.All metapackage, so you don’t have to install it.

This package and its dependencies (Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.Relational) provide runtime support for EF. You’ll add a tooling package later, in the Migrations tutorial.

For information about other database providers that are available for Entity Framework Core, see Database providers.

Create the data model

Next you’ll create entity classes for the Contoso University application. You’ll start with the following three entities.

Course-Enrollment-Student data model diagram
Course-Enrollment-Student data model diagram

There’s a one-to-many relationship between Student and Enrollment entities, and there’s a one-to-many relationship between Course and Enrollment entities. In other words, a student can be enrolled in any number of courses, and a course can have any number of students enrolled in it.

In the following sections you’ll create a class for each one of these entities.

The Student entity

Student entity diagram
Student entity diagram

In the Models folder, create a class file named Student.cs and replace the template code with the following code.

[!code-csharpMain]

   1:  #define AfterInheritance // or Intro or StringLength or DataType or BeforeInheritance
   2:   
   3:  #if Intro
   4:  #region snippet_Intro
   5:  using System;
   6:  using System.Collections.Generic;
   7:   
   8:  namespace ContosoUniversity.Models
   9:  {
  10:      public class Student
  11:      {
  12:          public int ID { get; set; }
  13:          public string LastName { get; set; }
  14:          public string FirstMidName { get; set; }
  15:          public DateTime EnrollmentDate { get; set; }
  16:   
  17:          public ICollection<Enrollment> Enrollments { get; set; }
  18:      }
  19:  }
  20:  #endregion
  21:   
  22:  #elif DataType
  23:  #region snippet_DataType
  24:  using System;
  25:  using System.Collections.Generic;
  26:  using System.ComponentModel.DataAnnotations;
  27:   
  28:  namespace ContosoUniversity.Models
  29:  {
  30:      public class Student
  31:      {
  32:          public int ID { get; set; }
  33:          public string LastName { get; set; }
  34:          public string FirstMidName { get; set; }
  35:          [DataType(DataType.Date)]
  36:          [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
  37:          public DateTime EnrollmentDate { get; set; }
  38:   
  39:          public ICollection<Enrollment> Enrollments { get; set; }
  40:      }
  41:  }
  42:  #endregion
  43:   
  44:  #elif StringLength
  45:  #region snippet_StringLength
  46:  using System;
  47:  using System.Collections.Generic;
  48:  using System.ComponentModel.DataAnnotations;
  49:   
  50:  namespace ContosoUniversity.Models
  51:  {
  52:      public class Student
  53:      {
  54:          public int ID { get; set; }
  55:          [StringLength(50)]
  56:          public string LastName { get; set; }
  57:          [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
  58:          public string FirstMidName { get; set; }
  59:          [DataType(DataType.Date)]
  60:          [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
  61:          public DateTime EnrollmentDate { get; set; }
  62:   
  63:          public ICollection<Enrollment> Enrollments { get; set; }
  64:      }
  65:  }
  66:  #endregion
  67:   
  68:  #elif Column
  69:  #region snippet_Column
  70:  using System;
  71:  using System.Collections.Generic;
  72:  using System.ComponentModel.DataAnnotations;
  73:  using System.ComponentModel.DataAnnotations.Schema;
  74:   
  75:  namespace ContosoUniversity.Models
  76:  {
  77:      public class Student
  78:      {
  79:          public int ID { get; set; }
  80:          [StringLength(50)]
  81:          public string LastName { get; set; }
  82:          [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
  83:          [Column("FirstName")]
  84:          public string FirstMidName { get; set; }
  85:          [DataType(DataType.Date)]
  86:          [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
  87:          public DateTime EnrollmentDate { get; set; }
  88:   
  89:          public ICollection<Enrollment> Enrollments { get; set; }
  90:      }
  91:  }
  92:  #endregion
  93:   
  94:   
  95:  #elif BeforeInheritance
  96:  #region snippet_BeforeInheritance
  97:  using System;
  98:  using System.Collections.Generic;
  99:  using System.ComponentModel.DataAnnotations;
 100:  using System.ComponentModel.DataAnnotations.Schema;
 101:   
 102:  namespace ContosoUniversity.Models
 103:  {
 104:      public class Student
 105:      {
 106:          public int ID { get; set; }
 107:          [Required]
 108:          [StringLength(50)]
 109:          [Display(Name = "Last Name")]
 110:          public string LastName { get; set; }
 111:          [Required]
 112:          [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
 113:          [Column("FirstName")]
 114:          [Display(Name = "First Name")]
 115:          public string FirstMidName { get; set; }
 116:          [DataType(DataType.Date)]
 117:          [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
 118:          [Display(Name = "Enrollment Date")]
 119:          public DateTime EnrollmentDate { get; set; }
 120:          [Display(Name = "Full Name")]
 121:          public string FullName
 122:          {
 123:              get
 124:              {
 125:                  return LastName + ", " + FirstMidName;
 126:              }
 127:          }
 128:   
 129:          public ICollection<Enrollment> Enrollments { get; set; }
 130:      }
 131:  }
 132:  #endregion
 133:  #elif AfterInheritance
 134:  #region snippet_AfterInheritance
 135:  using System;
 136:  using System.Collections.Generic;
 137:  using System.ComponentModel.DataAnnotations;
 138:  using System.ComponentModel.DataAnnotations.Schema;
 139:   
 140:  namespace ContosoUniversity.Models
 141:  {
 142:      public class Student : Person
 143:      {
 144:          [DataType(DataType.Date)]
 145:          [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
 146:          [Display(Name = "Enrollment Date")]
 147:          public DateTime EnrollmentDate { get; set; }
 148:   
 149:   
 150:          public ICollection<Enrollment> Enrollments { get; set; }
 151:      }
 152:  }
 153:  #endregion
 154:  #endif

The ID property will become the primary key column of the database table that corresponds to this class. By default, the Entity Framework interprets a property that’s named ID or classnameID as the primary key.

The Enrollments property is a navigation property. Navigation properties hold other entities that are related to this entity. In this case, the Enrollments property of a Student entity will hold all of the Enrollment entities that are related to that Student entity. In other words, if a given Student row in the database has two related Enrollment rows (rows that contain that student’s primary key value in their StudentID foreign key column), that Student entity’s Enrollments navigation property will contain those two Enrollment entities.

If a navigation property can hold multiple entities (as in many-to-many or one-to-many relationships), its type must be a list in which entries can be added, deleted, and updated, such as ICollection<T>. You can specify ICollection<T> or a type such as List<T> or HashSet<T>. If you specify ICollection<T>, EF creates a HashSet<T> collection by default.

The Enrollment entity

Enrollment entity diagram
Enrollment entity diagram

In the Models folder, create Enrollment.cs and replace the existing code with the following code:

[!code-csharpMain]

   1:  #define Final // or Intro
   2:   
   3:  #if Intro
   4:  #region snippet_Intro
   5:  namespace ContosoUniversity.Models
   6:  {
   7:      public enum Grade
   8:      {
   9:          A, B, C, D, F
  10:      }
  11:   
  12:      public class Enrollment
  13:      {
  14:          public int EnrollmentID { get; set; }
  15:          public int CourseID { get; set; }
  16:          public int StudentID { get; set; }
  17:          public Grade? Grade { get; set; }
  18:   
  19:          public Course Course { get; set; }
  20:          public Student Student { get; set; }
  21:      }
  22:  }
  23:  #endregion
  24:   
  25:  #elif Final
  26:  #region snippet_Final
  27:  using System.ComponentModel.DataAnnotations;
  28:  using System.ComponentModel.DataAnnotations.Schema;
  29:   
  30:  namespace ContosoUniversity.Models
  31:  {
  32:      public enum Grade
  33:      {
  34:          A, B, C, D, F
  35:      }
  36:   
  37:      public class Enrollment
  38:      {
  39:          public int EnrollmentID { get; set; }
  40:          public int CourseID { get; set; }
  41:          public int StudentID { get; set; }
  42:          [DisplayFormat(NullDisplayText = "No grade")]
  43:          public Grade? Grade { get; set; }
  44:   
  45:          public Course Course { get; set; }
  46:          public Student Student { get; set; }
  47:      }
  48:  }
  49:  #endregion
  50:  #endif

The EnrollmentID property will be the primary key; this entity uses the classnameID pattern instead of ID by itself as you saw in the Student entity. Ordinarily you would choose one pattern and use it throughout your data model. Here, the variation illustrates that you can use either pattern. In a later tutorial, you’ll see how using ID without classname makes it easier to implement inheritance in the data model.

The Grade property is an enum. The question mark after the Grade type declaration indicates that the Grade property is nullable. A grade that’s null is different from a zero grade – null means a grade isn’t known or hasn’t been assigned yet.

The StudentID property is a foreign key, and the corresponding navigation property is Student. An Enrollment entity is associated with one Student entity, so the property can only hold a single Student entity (unlike the Student.Enrollments navigation property you saw earlier, which can hold multiple Enrollment entities).

The CourseID property is a foreign key, and the corresponding navigation property is Course. An Enrollment entity is associated with one Course entity.

Entity Framework interprets a property as a foreign key property if it’s named <navigation property name><primary key property name> (for example, StudentID for the Student navigation property since the Student entity’s primary key is ID). Foreign key properties can also be named simply <primary key property name> (for example, CourseID since the Course entity’s primary key is CourseID).

The Course entity

Course entity diagram
Course entity diagram

In the Models folder, create Course.cs and replace the existing code with the following code:

[!code-csharpMain]

   1:  #define Final // or Intro
   2:   
   3:  #if Intro
   4:  #region snippet_Intro
   5:  using System.Collections.Generic;
   6:  using System.ComponentModel.DataAnnotations.Schema;
   7:   
   8:  namespace ContosoUniversity.Models
   9:  {
  10:      public class Course
  11:      {
  12:          [DatabaseGenerated(DatabaseGeneratedOption.None)]
  13:          public int CourseID { get; set; }
  14:          public string Title { get; set; }
  15:          public int Credits { get; set; }
  16:   
  17:          public ICollection<Enrollment> Enrollments { get; set; }
  18:      }
  19:  }
  20:  #endregion
  21:   
  22:  #elif Final
  23:  #region snippet_Final
  24:  using System.Collections.Generic;
  25:  using System.ComponentModel.DataAnnotations;
  26:  using System.ComponentModel.DataAnnotations.Schema;
  27:   
  28:  namespace ContosoUniversity.Models
  29:  {
  30:      public class Course
  31:      {
  32:          [DatabaseGenerated(DatabaseGeneratedOption.None)]
  33:          [Display(Name = "Number")]
  34:          public int CourseID { get; set; }
  35:   
  36:          [StringLength(50, MinimumLength = 3)]
  37:          public string Title { get; set; }
  38:   
  39:          [Range(0, 5)]
  40:          public int Credits { get; set; }
  41:   
  42:          public int DepartmentID { get; set; }
  43:   
  44:          public Department Department { get; set; }
  45:          public ICollection<Enrollment> Enrollments { get; set; }
  46:          public ICollection<CourseAssignment> CourseAssignments { get; set; }
  47:      }
  48:  }
  49:  #endregion
  50:  #endif

The Enrollments property is a navigation property. A Course entity can be related to any number of Enrollment entities.

We’ll say more about the DatabaseGenerated attribute in a later tutorial in this series. Basically, this attribute lets you enter the primary key for the course rather than having the database generate it.

Create the Database Context

The main class that coordinates Entity Framework functionality for a given data model is the database context class. You create this class by deriving from the Microsoft.EntityFrameworkCore.DbContext class. In your code you specify which entities are included in the data model. You can also customize certain Entity Framework behavior. In this project, the class is named SchoolContext.

In the project folder, create a folder named Data.

In the Data folder create a new class file named SchoolContext.cs, and replace the template code with the following code:

[!code-csharpMain]

   1:  #define AfterInheritance // or Intro or TableNames or BeforeInheritance
   2:   
   3:  #if Intro
   4:  #region snippet_Intro
   5:  using ContosoUniversity.Models;
   6:  using Microsoft.EntityFrameworkCore;
   7:   
   8:  namespace ContosoUniversity.Data
   9:  {
  10:      public class SchoolContext : DbContext
  11:      {
  12:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  13:          {
  14:          }
  15:   
  16:          public DbSet<Course> Courses { get; set; }
  17:          public DbSet<Enrollment> Enrollments { get; set; }
  18:          public DbSet<Student> Students { get; set; }
  19:      }
  20:  }
  21:  #endregion
  22:   
  23:  #elif TableNames
  24:  #region snippet_TableNames
  25:  using ContosoUniversity.Models;
  26:  using Microsoft.EntityFrameworkCore;
  27:   
  28:  namespace ContosoUniversity.Data
  29:  {
  30:      public class SchoolContext : DbContext
  31:      {
  32:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  33:          {
  34:          }
  35:   
  36:          public DbSet<Course> Courses { get; set; }
  37:          public DbSet<Enrollment> Enrollments { get; set; }
  38:          public DbSet<Student> Students { get; set; }
  39:   
  40:          protected override void OnModelCreating(ModelBuilder modelBuilder)
  41:          {
  42:              modelBuilder.Entity<Course>().ToTable("Course");
  43:              modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
  44:              modelBuilder.Entity<Student>().ToTable("Student");
  45:          }
  46:      }
  47:  }
  48:  #endregion
  49:   
  50:  #elif BeforeInheritance
  51:  #region snippet_BeforeInheritance
  52:  using ContosoUniversity.Models;
  53:  using Microsoft.EntityFrameworkCore;
  54:   
  55:  namespace ContosoUniversity.Data
  56:  {
  57:      public class SchoolContext : DbContext
  58:      {
  59:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  60:          {
  61:          }
  62:   
  63:          public DbSet<Course> Courses { get; set; }
  64:          public DbSet<Enrollment> Enrollments { get; set; }
  65:          public DbSet<Student> Students { get; set; }
  66:          public DbSet<Department> Departments { get; set; }
  67:          public DbSet<Instructor> Instructors { get; set; }
  68:          public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
  69:          public DbSet<CourseAssignment> CourseAssignments { get; set; }
  70:   
  71:          protected override void OnModelCreating(ModelBuilder modelBuilder)
  72:          {
  73:              modelBuilder.Entity<Course>().ToTable("Course");
  74:              modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
  75:              modelBuilder.Entity<Student>().ToTable("Student");
  76:              modelBuilder.Entity<Department>().ToTable("Department");
  77:              modelBuilder.Entity<Instructor>().ToTable("Instructor");
  78:              modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
  79:              modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
  80:   
  81:              modelBuilder.Entity<CourseAssignment>()
  82:                  .HasKey(c => new { c.CourseID, c.InstructorID });
  83:          }
  84:      }
  85:  }
  86:  #endregion
  87:  #elif AfterInheritance
  88:  #region snippet_AfterInheritance
  89:  using ContosoUniversity.Models;
  90:  using Microsoft.EntityFrameworkCore;
  91:   
  92:  namespace ContosoUniversity.Data
  93:  {
  94:      public class SchoolContext : DbContext
  95:      {
  96:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  97:          {
  98:          }
  99:   
 100:          public DbSet<Course> Courses { get; set; }
 101:          public DbSet<Enrollment> Enrollments { get; set; }
 102:          public DbSet<Student> Students { get; set; }
 103:          public DbSet<Department> Departments { get; set; }
 104:          public DbSet<Instructor> Instructors { get; set; }
 105:          public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
 106:          public DbSet<CourseAssignment> CourseAssignments { get; set; }
 107:          public DbSet<Person> People { get; set; }
 108:   
 109:          protected override void OnModelCreating(ModelBuilder modelBuilder)
 110:          {
 111:              modelBuilder.Entity<Course>().ToTable("Course");
 112:              modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
 113:              modelBuilder.Entity<Student>().ToTable("Student");
 114:              modelBuilder.Entity<Department>().ToTable("Department");
 115:              modelBuilder.Entity<Instructor>().ToTable("Instructor");
 116:              modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
 117:              modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
 118:              modelBuilder.Entity<Person>().ToTable("Person");
 119:   
 120:              modelBuilder.Entity<CourseAssignment>()
 121:                  .HasKey(c => new { c.CourseID, c.InstructorID });
 122:          }
 123:      }
 124:  }
 125:  #endregion
 126:  #endif

This code creates a DbSet property for each entity set. In Entity Framework terminology, an entity set typically corresponds to a database table, and an entity corresponds to a row in the table.

You could have omitted the DbSet<Enrollment> and DbSet<Course> statements and it would work the same. The Entity Framework would include them implicitly because the Student entity references the Enrollment entity and the Enrollment entity references the Course entity.

When the database is created, EF creates tables that have names the same as the DbSet property names. Property names for collections are typically plural (Students rather than Student), but developers disagree about whether table names should be pluralized or not. For these tutorials you’ll override the default behavior by specifying singular table names in the DbContext. To do that, add the following highlighted code after the last DbSet property.

[!code-csharpMain]

   1:  #define AfterInheritance // or Intro or TableNames or BeforeInheritance
   2:   
   3:  #if Intro
   4:  #region snippet_Intro
   5:  using ContosoUniversity.Models;
   6:  using Microsoft.EntityFrameworkCore;
   7:   
   8:  namespace ContosoUniversity.Data
   9:  {
  10:      public class SchoolContext : DbContext
  11:      {
  12:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  13:          {
  14:          }
  15:   
  16:          public DbSet<Course> Courses { get; set; }
  17:          public DbSet<Enrollment> Enrollments { get; set; }
  18:          public DbSet<Student> Students { get; set; }
  19:      }
  20:  }
  21:  #endregion
  22:   
  23:  #elif TableNames
  24:  #region snippet_TableNames
  25:  using ContosoUniversity.Models;
  26:  using Microsoft.EntityFrameworkCore;
  27:   
  28:  namespace ContosoUniversity.Data
  29:  {
  30:      public class SchoolContext : DbContext
  31:      {
  32:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  33:          {
  34:          }
  35:   
  36:          public DbSet<Course> Courses { get; set; }
  37:          public DbSet<Enrollment> Enrollments { get; set; }
  38:          public DbSet<Student> Students { get; set; }
  39:   
  40:          protected override void OnModelCreating(ModelBuilder modelBuilder)
  41:          {
  42:              modelBuilder.Entity<Course>().ToTable("Course");
  43:              modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
  44:              modelBuilder.Entity<Student>().ToTable("Student");
  45:          }
  46:      }
  47:  }
  48:  #endregion
  49:   
  50:  #elif BeforeInheritance
  51:  #region snippet_BeforeInheritance
  52:  using ContosoUniversity.Models;
  53:  using Microsoft.EntityFrameworkCore;
  54:   
  55:  namespace ContosoUniversity.Data
  56:  {
  57:      public class SchoolContext : DbContext
  58:      {
  59:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  60:          {
  61:          }
  62:   
  63:          public DbSet<Course> Courses { get; set; }
  64:          public DbSet<Enrollment> Enrollments { get; set; }
  65:          public DbSet<Student> Students { get; set; }
  66:          public DbSet<Department> Departments { get; set; }
  67:          public DbSet<Instructor> Instructors { get; set; }
  68:          public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
  69:          public DbSet<CourseAssignment> CourseAssignments { get; set; }
  70:   
  71:          protected override void OnModelCreating(ModelBuilder modelBuilder)
  72:          {
  73:              modelBuilder.Entity<Course>().ToTable("Course");
  74:              modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
  75:              modelBuilder.Entity<Student>().ToTable("Student");
  76:              modelBuilder.Entity<Department>().ToTable("Department");
  77:              modelBuilder.Entity<Instructor>().ToTable("Instructor");
  78:              modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
  79:              modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
  80:   
  81:              modelBuilder.Entity<CourseAssignment>()
  82:                  .HasKey(c => new { c.CourseID, c.InstructorID });
  83:          }
  84:      }
  85:  }
  86:  #endregion
  87:  #elif AfterInheritance
  88:  #region snippet_AfterInheritance
  89:  using ContosoUniversity.Models;
  90:  using Microsoft.EntityFrameworkCore;
  91:   
  92:  namespace ContosoUniversity.Data
  93:  {
  94:      public class SchoolContext : DbContext
  95:      {
  96:          public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
  97:          {
  98:          }
  99:   
 100:          public DbSet<Course> Courses { get; set; }
 101:          public DbSet<Enrollment> Enrollments { get; set; }
 102:          public DbSet<Student> Students { get; set; }
 103:          public DbSet<Department> Departments { get; set; }
 104:          public DbSet<Instructor> Instructors { get; set; }
 105:          public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
 106:          public DbSet<CourseAssignment> CourseAssignments { get; set; }
 107:          public DbSet<Person> People { get; set; }
 108:   
 109:          protected override void OnModelCreating(ModelBuilder modelBuilder)
 110:          {
 111:              modelBuilder.Entity<Course>().ToTable("Course");
 112:              modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
 113:              modelBuilder.Entity<Student>().ToTable("Student");
 114:              modelBuilder.Entity<Department>().ToTable("Department");
 115:              modelBuilder.Entity<Instructor>().ToTable("Instructor");
 116:              modelBuilder.Entity<OfficeAssignment>().ToTable("OfficeAssignment");
 117:              modelBuilder.Entity<CourseAssignment>().ToTable("CourseAssignment");
 118:              modelBuilder.Entity<Person>().ToTable("Person");
 119:   
 120:              modelBuilder.Entity<CourseAssignment>()
 121:                  .HasKey(c => new { c.CourseID, c.InstructorID });
 122:          }
 123:      }
 124:  }
 125:  #endregion
 126:  #endif

Register the context with dependency injection

ASP.NET Core implements dependency injection by default. Services (such as the EF database context) are registered with dependency injection during application startup. Components that require these services (such as MVC controllers) are provided these services via constructor parameters. You’ll see the controller constructor code that gets a context instance later in this tutorial.

To register SchoolContext as a service, open Startup.cs, and add the highlighted lines to the ConfigureServices method.

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Threading.Tasks;
   5:  using Microsoft.AspNetCore.Builder;
   6:  using Microsoft.AspNetCore.Hosting;
   7:  using Microsoft.Extensions.Configuration;
   8:  using Microsoft.Extensions.DependencyInjection;
   9:  #region snippet_Usings
  10:  using ContosoUniversity.Data;
  11:  using Microsoft.EntityFrameworkCore;
  12:  #endregion
  13:   
  14:  namespace ContosoUniversity
  15:  {
  16:      public class Startup
  17:      {
  18:          public Startup(IConfiguration configuration)
  19:          {
  20:              Configuration = configuration;
  21:          }
  22:   
  23:          public IConfiguration Configuration { get; }
  24:   
  25:          // This method gets called by the runtime. Use this method to add services to the container.
  26:          #region snippet_SchoolContext
  27:          public void ConfigureServices(IServiceCollection services)
  28:          {
  29:              services.AddDbContext<SchoolContext>(options =>
  30:                  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  31:   
  32:              services.AddMvc();
  33:          }
  34:          #endregion
  35:   
  36:          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  37:          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  38:          {
  39:              if (env.IsDevelopment())
  40:              {
  41:                  app.UseDeveloperExceptionPage();
  42:                  app.UseBrowserLink();
  43:              }
  44:              else
  45:              {
  46:                  app.UseExceptionHandler("/Home/Error");
  47:              }
  48:   
  49:              app.UseStaticFiles();
  50:   
  51:              #region snippet_Route
  52:              app.UseMvc(routes =>
  53:              {
  54:                  routes.MapRoute(
  55:                      name: "default",
  56:                      template: "{controller=Home}/{action=Index}/{id?}");
  57:              });
  58:              #endregion
  59:          }
  60:      }
  61:  }

The name of the connection string is passed in to the context by calling a method on a DbContextOptionsBuilder object. For local development, the (xref:)ASP.NET Core configuration system reads the connection string from the appsettings.json file.

Add using statements for ContosoUniversity.Data and Microsoft.EntityFrameworkCore namespaces, and then build the project.

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Threading.Tasks;
   5:  using Microsoft.AspNetCore.Builder;
   6:  using Microsoft.AspNetCore.Hosting;
   7:  using Microsoft.Extensions.Configuration;
   8:  using Microsoft.Extensions.DependencyInjection;
   9:  #region snippet_Usings
  10:  using ContosoUniversity.Data;
  11:  using Microsoft.EntityFrameworkCore;
  12:  #endregion
  13:   
  14:  namespace ContosoUniversity
  15:  {
  16:      public class Startup
  17:      {
  18:          public Startup(IConfiguration configuration)
  19:          {
  20:              Configuration = configuration;
  21:          }
  22:   
  23:          public IConfiguration Configuration { get; }
  24:   
  25:          // This method gets called by the runtime. Use this method to add services to the container.
  26:          #region snippet_SchoolContext
  27:          public void ConfigureServices(IServiceCollection services)
  28:          {
  29:              services.AddDbContext<SchoolContext>(options =>
  30:                  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  31:   
  32:              services.AddMvc();
  33:          }
  34:          #endregion
  35:   
  36:          // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  37:          public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  38:          {
  39:              if (env.IsDevelopment())
  40:              {
  41:                  app.UseDeveloperExceptionPage();
  42:                  app.UseBrowserLink();
  43:              }
  44:              else
  45:              {
  46:                  app.UseExceptionHandler("/Home/Error");
  47:              }
  48:   
  49:              app.UseStaticFiles();
  50:   
  51:              #region snippet_Route
  52:              app.UseMvc(routes =>
  53:              {
  54:                  routes.MapRoute(
  55:                      name: "default",
  56:                      template: "{controller=Home}/{action=Index}/{id?}");
  57:              });
  58:              #endregion
  59:          }
  60:      }
  61:  }

Open the appsettings.json file and add a connection string as shown in the following example.

[!code-jsonMain]

   1:  {
   2:    "ConnectionStrings": {
   3:      "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
   4:    },
   5:    "Logging": {
   6:      "IncludeScopes": false,
   7:      "LogLevel": {
   8:        "Default": "Warning"
   9:      }
  10:    }
  11:  }

SQL Server Express LocalDB

The connection string specifies a SQL Server LocalDB database. LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for application development, not production use. LocalDB starts on demand and runs in user mode, so there is no complex configuration. By default, LocalDB creates .mdf database files in the C:/Users/<user> directory.

Add code to initialize the database with test data

The Entity Framework will create an empty database for you. In this section, you write a method that is called after the database is created in order to populate it with test data.

Here you’ll use the EnsureCreated method to automatically create the database. In a later tutorial you’ll see how to handle model changes by using Code First Migrations to change the database schema instead of dropping and re-creating the database.

In the Data folder, create a new class file named DbInitializer.cs and replace the template code with the following code, which causes a database to be created when needed and loads test data into the new database.

[!code-csharpMain]

   1:  #define Final // or Intro
   2:   
   3:  #if Intro
   4:  #region snippet_Intro
   5:  using ContosoUniversity.Models;
   6:  using System;
   7:  using System.Linq;
   8:   
   9:  namespace ContosoUniversity.Data
  10:  {
  11:      public static class DbInitializer
  12:      {
  13:          public static void Initialize(SchoolContext context)
  14:          {
  15:              context.Database.EnsureCreated();
  16:   
  17:              // Look for any students.
  18:              if (context.Students.Any())
  19:              {
  20:                  return;   // DB has been seeded
  21:              }
  22:   
  23:              var students = new Student[]
  24:              {
  25:              new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
  26:              new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
  27:              new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
  28:              new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
  29:              new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
  30:              new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
  31:              new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
  32:              new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
  33:              };
  34:              foreach (Student s in students)
  35:              {
  36:                  context.Students.Add(s);
  37:              }
  38:              context.SaveChanges();
  39:   
  40:              var courses = new Course[]
  41:              {
  42:              new Course{CourseID=1050,Title="Chemistry",Credits=3},
  43:              new Course{CourseID=4022,Title="Microeconomics",Credits=3},
  44:              new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
  45:              new Course{CourseID=1045,Title="Calculus",Credits=4},
  46:              new Course{CourseID=3141,Title="Trigonometry",Credits=4},
  47:              new Course{CourseID=2021,Title="Composition",Credits=3},
  48:              new Course{CourseID=2042,Title="Literature",Credits=4}
  49:              };
  50:              foreach (Course c in courses)
  51:              {
  52:                  context.Courses.Add(c);
  53:              }
  54:              context.SaveChanges();
  55:   
  56:              var enrollments = new Enrollment[]
  57:              {
  58:              new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
  59:              new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
  60:              new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
  61:              new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
  62:              new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
  63:              new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
  64:              new Enrollment{StudentID=3,CourseID=1050},
  65:              new Enrollment{StudentID=4,CourseID=1050},
  66:              new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
  67:              new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
  68:              new Enrollment{StudentID=6,CourseID=1045},
  69:              new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
  70:              };
  71:              foreach (Enrollment e in enrollments)
  72:              {
  73:                  context.Enrollments.Add(e);
  74:              }
  75:              context.SaveChanges();
  76:          }
  77:      }
  78:  }
  79:  #endregion
  80:   
  81:  #elif Final
  82:  #region snippet_Final
  83:  using System;
  84:  using System.Linq;
  85:  using Microsoft.EntityFrameworkCore;
  86:  using Microsoft.Extensions.DependencyInjection;
  87:  using ContosoUniversity.Models;
  88:   
  89:  namespace ContosoUniversity.Data
  90:  {
  91:      public static class DbInitializer
  92:      {
  93:          public static void Initialize(SchoolContext context)
  94:          {
  95:              //context.Database.EnsureCreated();
  96:   
  97:              // Look for any students.
  98:              if (context.Students.Any())
  99:              {
 100:                  return;   // DB has been seeded
 101:              }
 102:   
 103:              var students = new Student[]
 104:              {
 105:                  new Student { FirstMidName = "Carson",   LastName = "Alexander",
 106:                      EnrollmentDate = DateTime.Parse("2010-09-01") },
 107:                  new Student { FirstMidName = "Meredith", LastName = "Alonso",
 108:                      EnrollmentDate = DateTime.Parse("2012-09-01") },
 109:                  new Student { FirstMidName = "Arturo",   LastName = "Anand",
 110:                      EnrollmentDate = DateTime.Parse("2013-09-01") },
 111:                  new Student { FirstMidName = "Gytis",    LastName = "Barzdukas",
 112:                      EnrollmentDate = DateTime.Parse("2012-09-01") },
 113:                  new Student { FirstMidName = "Yan",      LastName = "Li",
 114:                      EnrollmentDate = DateTime.Parse("2012-09-01") },
 115:                  new Student { FirstMidName = "Peggy",    LastName = "Justice",
 116:                      EnrollmentDate = DateTime.Parse("2011-09-01") },
 117:                  new Student { FirstMidName = "Laura",    LastName = "Norman",
 118:                      EnrollmentDate = DateTime.Parse("2013-09-01") },
 119:                  new Student { FirstMidName = "Nino",     LastName = "Olivetto",
 120:                      EnrollmentDate = DateTime.Parse("2005-09-01") }
 121:              };
 122:   
 123:              foreach (Student s in students)
 124:              {
 125:                  context.Students.Add(s);
 126:              }
 127:              context.SaveChanges();
 128:   
 129:              var instructors = new Instructor[]
 130:              {
 131:                  new Instructor { FirstMidName = "Kim",     LastName = "Abercrombie",
 132:                      HireDate = DateTime.Parse("1995-03-11") },
 133:                  new Instructor { FirstMidName = "Fadi",    LastName = "Fakhouri",
 134:                      HireDate = DateTime.Parse("2002-07-06") },
 135:                  new Instructor { FirstMidName = "Roger",   LastName = "Harui",
 136:                      HireDate = DateTime.Parse("1998-07-01") },
 137:                  new Instructor { FirstMidName = "Candace", LastName = "Kapoor",
 138:                      HireDate = DateTime.Parse("2001-01-15") },
 139:                  new Instructor { FirstMidName = "Roger",   LastName = "Zheng",
 140:                      HireDate = DateTime.Parse("2004-02-12") }
 141:              };
 142:   
 143:              foreach (Instructor i in instructors)
 144:              {
 145:                  context.Instructors.Add(i);
 146:              }
 147:              context.SaveChanges();
 148:   
 149:              var departments = new Department[]
 150:              {
 151:                  new Department { Name = "English",     Budget = 350000,
 152:                      StartDate = DateTime.Parse("2007-09-01"),
 153:                      InstructorID  = instructors.Single( i => i.LastName == "Abercrombie").ID },
 154:                  new Department { Name = "Mathematics", Budget = 100000,
 155:                      StartDate = DateTime.Parse("2007-09-01"),
 156:                      InstructorID  = instructors.Single( i => i.LastName == "Fakhouri").ID },
 157:                  new Department { Name = "Engineering", Budget = 350000,
 158:                      StartDate = DateTime.Parse("2007-09-01"),
 159:                      InstructorID  = instructors.Single( i => i.LastName == "Harui").ID },
 160:                  new Department { Name = "Economics",   Budget = 100000,
 161:                      StartDate = DateTime.Parse("2007-09-01"),
 162:                      InstructorID  = instructors.Single( i => i.LastName == "Kapoor").ID }
 163:              };
 164:   
 165:              foreach (Department d in departments)
 166:              {
 167:                  context.Departments.Add(d);
 168:              }
 169:              context.SaveChanges();
 170:   
 171:              var courses = new Course[]
 172:              {
 173:                  new Course {CourseID = 1050, Title = "Chemistry",      Credits = 3,
 174:                      DepartmentID = departments.Single( s => s.Name == "Engineering").DepartmentID
 175:                  },
 176:                  new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3,
 177:                      DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID
 178:                  },
 179:                  new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3,
 180:                      DepartmentID = departments.Single( s => s.Name == "Economics").DepartmentID
 181:                  },
 182:                  new Course {CourseID = 1045, Title = "Calculus",       Credits = 4,
 183:                      DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID
 184:                  },
 185:                  new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4,
 186:                      DepartmentID = departments.Single( s => s.Name == "Mathematics").DepartmentID
 187:                  },
 188:                  new Course {CourseID = 2021, Title = "Composition",    Credits = 3,
 189:                      DepartmentID = departments.Single( s => s.Name == "English").DepartmentID
 190:                  },
 191:                  new Course {CourseID = 2042, Title = "Literature",     Credits = 4,
 192:                      DepartmentID = departments.Single( s => s.Name == "English").DepartmentID
 193:                  },
 194:              };
 195:   
 196:              foreach (Course c in courses)
 197:              {
 198:                  context.Courses.Add(c);
 199:              }
 200:              context.SaveChanges();
 201:   
 202:              var officeAssignments = new OfficeAssignment[]
 203:              {
 204:                  new OfficeAssignment {
 205:                      InstructorID = instructors.Single( i => i.LastName == "Fakhouri").ID,
 206:                      Location = "Smith 17" },
 207:                  new OfficeAssignment {
 208:                      InstructorID = instructors.Single( i => i.LastName == "Harui").ID,
 209:                      Location = "Gowan 27" },
 210:                  new OfficeAssignment {
 211:                      InstructorID = instructors.Single( i => i.LastName == "Kapoor").ID,
 212:                      Location = "Thompson 304" },
 213:              };
 214:   
 215:              foreach (OfficeAssignment o in officeAssignments)
 216:              {
 217:                  context.OfficeAssignments.Add(o);
 218:              }
 219:              context.SaveChanges();
 220:   
 221:              var courseInstructors = new CourseAssignment[]
 222:              {
 223:                  new CourseAssignment {
 224:                      CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
 225:                      InstructorID = instructors.Single(i => i.LastName == "Kapoor").ID
 226:                      },
 227:                  new CourseAssignment {
 228:                      CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
 229:                      InstructorID = instructors.Single(i => i.LastName == "Harui").ID
 230:                      },
 231:                  new CourseAssignment {
 232:                      CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
 233:                      InstructorID = instructors.Single(i => i.LastName == "Zheng").ID
 234:                      },
 235:                  new CourseAssignment {
 236:                      CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
 237:                      InstructorID = instructors.Single(i => i.LastName == "Zheng").ID
 238:                      },
 239:                  new CourseAssignment {
 240:                      CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
 241:                      InstructorID = instructors.Single(i => i.LastName == "Fakhouri").ID
 242:                      },
 243:                  new CourseAssignment {
 244:                      CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
 245:                      InstructorID = instructors.Single(i => i.LastName == "Harui").ID
 246:                      },
 247:                  new CourseAssignment {
 248:                      CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
 249:                      InstructorID = instructors.Single(i => i.LastName == "Abercrombie").ID
 250:                      },
 251:                  new CourseAssignment {
 252:                      CourseID = courses.Single(c => c.Title == "Literature" ).CourseID,
 253:                      InstructorID = instructors.Single(i => i.LastName == "Abercrombie").ID
 254:                      },
 255:              };
 256:   
 257:              foreach (CourseAssignment ci in courseInstructors)
 258:              {
 259:                  context.CourseAssignments.Add(ci);
 260:              }
 261:              context.SaveChanges();
 262:   
 263:              var enrollments = new Enrollment[]
 264:              {
 265:                  new Enrollment {
 266:                      StudentID = students.Single(s => s.LastName == "Alexander").ID,
 267:                      CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
 268:                      Grade = Grade.A
 269:                  },
 270:                      new Enrollment {
 271:                      StudentID = students.Single(s => s.LastName == "Alexander").ID,
 272:                      CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
 273:                      Grade = Grade.C
 274:                      },
 275:                      new Enrollment {
 276:                      StudentID = students.Single(s => s.LastName == "Alexander").ID,
 277:                      CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
 278:                      Grade = Grade.B
 279:                      },
 280:                      new Enrollment {
 281:                          StudentID = students.Single(s => s.LastName == "Alonso").ID,
 282:                      CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
 283:                      Grade = Grade.B
 284:                      },
 285:                      new Enrollment {
 286:                          StudentID = students.Single(s => s.LastName == "Alonso").ID,
 287:                      CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
 288:                      Grade = Grade.B
 289:                      },
 290:                      new Enrollment {
 291:                      StudentID = students.Single(s => s.LastName == "Alonso").ID,
 292:                      CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
 293:                      Grade = Grade.B
 294:                      },
 295:                      new Enrollment {
 296:                      StudentID = students.Single(s => s.LastName == "Anand").ID,
 297:                      CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
 298:                      },
 299:                      new Enrollment {
 300:                      StudentID = students.Single(s => s.LastName == "Anand").ID,
 301:                      CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
 302:                      Grade = Grade.B
 303:                      },
 304:                  new Enrollment {
 305:                      StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
 306:                      CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
 307:                      Grade = Grade.B
 308:                      },
 309:                      new Enrollment {
 310:                      StudentID = students.Single(s => s.LastName == "Li").ID,
 311:                      CourseID = courses.Single(c => c.Title == "Composition").CourseID,
 312:                      Grade = Grade.B
 313:                      },
 314:                      new Enrollment {
 315:                      StudentID = students.Single(s => s.LastName == "Justice").ID,
 316:                      CourseID = courses.Single(c => c.Title == "Literature").CourseID,
 317:                      Grade = Grade.B
 318:                      }
 319:              };
 320:   
 321:              foreach (Enrollment e in enrollments)
 322:              {
 323:                  var enrollmentInDataBase = context.Enrollments.Where(
 324:                      s =>
 325:                              s.Student.ID == e.StudentID &&
 326:                              s.Course.CourseID == e.CourseID).SingleOrDefault();
 327:                  if (enrollmentInDataBase == null)
 328:                  {
 329:                      context.Enrollments.Add(e);
 330:                  }
 331:              }
 332:              context.SaveChanges();
 333:          }
 334:      }
 335:  }
 336:  #endregion
 337:  #endif

The code checks if there are any students in the database, and if not, it assumes the database is new and needs to be seeded with test data. It loads test data into arrays rather than List<T> collections to optimize performance.

In Program.cs, modify the Main method to do the following on application startup:

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.IO;
   4:  using System.Linq;
   5:  using System.Threading.Tasks;
   6:  using Microsoft.AspNetCore;
   7:  using Microsoft.AspNetCore.Hosting;
   8:  using Microsoft.Extensions.Configuration;
   9:  using Microsoft.Extensions.Logging;
  10:  #region snippet_Usings
  11:  using Microsoft.Extensions.DependencyInjection;
  12:  using ContosoUniversity.Data;
  13:  #endregion
  14:   
  15:  namespace ContosoUniversity
  16:  {
  17:      public class Program
  18:      {
  19:  #region snippet_Seed
  20:          public static void Main(string[] args)
  21:          {
  22:              var host = BuildWebHost(args);
  23:   
  24:              using (var scope = host.Services.CreateScope())
  25:              {
  26:                  var services = scope.ServiceProvider;
  27:                  try
  28:                  {
  29:                      var context = services.GetRequiredService<SchoolContext>();
  30:                      DbInitializer.Initialize(context);
  31:                  }
  32:                  catch (Exception ex)
  33:                  {
  34:                      var logger = services.GetRequiredService<ILogger<Program>>();
  35:                      logger.LogError(ex, "An error occurred while seeding the database.");
  36:                  }
  37:              }
  38:   
  39:              host.Run();
  40:          }
  41:  #endregion
  42:   
  43:          public static IWebHost BuildWebHost(string[] args) =>
  44:              WebHost.CreateDefaultBuilder(args)
  45:                  .UseStartup<Startup>()
  46:                  .Build();
  47:      }
  48:  }

Add using statements:

[!code-csharpMain]

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.IO;
   4:  using System.Linq;
   5:  using System.Threading.Tasks;
   6:  using Microsoft.AspNetCore;
   7:  using Microsoft.AspNetCore.Hosting;
   8:  using Microsoft.Extensions.Configuration;
   9:  using Microsoft.Extensions.Logging;
  10:  #region snippet_Usings
  11:  using Microsoft.Extensions.DependencyInjection;
  12:  using ContosoUniversity.Data;
  13:  #endregion
  14:   
  15:  namespace ContosoUniversity
  16:  {
  17:      public class Program
  18:      {
  19:  #region snippet_Seed
  20:          public static void Main(string[] args)
  21:          {
  22:              var host = BuildWebHost(args);
  23:   
  24:              using (var scope = host.Services.CreateScope())
  25:              {
  26:                  var services = scope.ServiceProvider;
  27:                  try
  28:                  {
  29:                      var context = services.GetRequiredService<SchoolContext>();
  30:                      DbInitializer.Initialize(context);
  31:                  }
  32:                  catch (Exception ex)
  33:                  {
  34:                      var logger = services.GetRequiredService<ILogger<Program>>();
  35:                      logger.LogError(ex, "An error occurred while seeding the database.");
  36:                  }
  37:              }
  38:   
  39:              host.Run();
  40:          }
  41:  #endregion
  42:   
  43:          public static IWebHost BuildWebHost(string[] args) =>
  44:              WebHost.CreateDefaultBuilder(args)
  45:                  .UseStartup<Startup>()
  46:                  .Build();
  47:      }
  48:  }

In older tutorials, you may see similar code in the Configure method in Startup.cs. We recommend that you use the Configure method only to set up the request pipeline. Application startup code belongs in the Main method.

Now the first time you run the application, the database will be created and seeded with test data. Whenever you change your data model, you can delete the database, update your seed method, and start afresh with a new database the same way. In later tutorials, you’ll see how to modify the database when the data model changes, without deleting and re-creating it.

Create a controller and views

Next, you’ll use the scaffolding engine in Visual Studio to add an MVC controller and views that will use EF to query and save data.

The automatic creation of CRUD action methods and views is known as scaffolding. Scaffolding differs from code generation in that the scaffolded code is a starting point that you can modify to suit your own requirements, whereas you typically don’t modify generated code. When you need to customize generated code, you use partial classes or you regenerate the code when things change.

If the Add MVC Dependencies dialog appears:

(The scaffolding engine can also create the database context for you if you don’t create it manually first as you did earlier for this tutorial. You can specify a new context class in the Add Controller box by clicking the plus sign to the right of Data context class. Visual Studio will then create your DbContext class as well as the controller and views.)

You’ll notice that the controller takes a SchoolContext as a constructor parameter.

[!code-csharpMain]

   1:  #define SortFilterPage //or ScaffoldedIndex or SortOnly or SortFilter or DynamicLinq
   2:  #define ReadFirst //or CreateAndAttach
   3:  #define DeleteWithReadFirst // or DeleteWithoutReadFirst
   4:   
   5:  using System.Linq;
   6:  using System.Threading.Tasks;
   7:  using Microsoft.AspNetCore.Mvc;
   8:  using Microsoft.AspNetCore.Mvc.Rendering;
   9:  using Microsoft.EntityFrameworkCore;
  10:  using ContosoUniversity.Data;
  11:  using ContosoUniversity.Models;
  12:  using System;
  13:  using Microsoft.Extensions.Logging;
  14:   
  15:  #region snippet_Context
  16:  namespace ContosoUniversity.Controllers
  17:  {
  18:      public class StudentsController : Controller
  19:      {
  20:          private readonly SchoolContext _context;
  21:   
  22:          public StudentsController(SchoolContext context)
  23:          {
  24:              _context = context;
  25:          }
  26:  #endregion
  27:   
  28:          // GET: Students
  29:   
  30:  #if (ScaffoldedIndex)
  31:  #region snippet_ScaffoldedIndex
  32:          public async Task<IActionResult> Index()
  33:          {
  34:              return View(await _context.Students.ToListAsync());
  35:          }
  36:  #endregion
  37:  #elif (SortOnly)
  38:  #region snippet_SortOnly
  39:          public async Task<IActionResult> Index(string sortOrder)
  40:          {
  41:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  42:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  43:              var students = from s in _context.Students
  44:                             select s;
  45:              switch (sortOrder)
  46:              {
  47:                  case "name_desc":
  48:                      students = students.OrderByDescending(s => s.LastName);
  49:                      break;
  50:                  case "Date":
  51:                      students = students.OrderBy(s => s.EnrollmentDate);
  52:                      break;
  53:                  case "date_desc":
  54:                      students = students.OrderByDescending(s => s.EnrollmentDate);
  55:                      break;
  56:                  default:
  57:                      students = students.OrderBy(s => s.LastName);
  58:                      break;
  59:              }
  60:              return View(await students.AsNoTracking().ToListAsync());
  61:          }
  62:  #endregion
  63:  #elif (SortFilter)
  64:  #region snippet_SortFilter
  65:          public async Task<IActionResult> Index(string sortOrder, string searchString)
  66:          {
  67:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  68:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  69:              ViewData["CurrentFilter"] = searchString;
  70:   
  71:              var students = from s in _context.Students
  72:                             select s;
  73:              if (!String.IsNullOrEmpty(searchString))
  74:              {
  75:                  students = students.Where(s => s.LastName.Contains(searchString)
  76:                                         || s.FirstMidName.Contains(searchString));
  77:              }
  78:              switch (sortOrder)
  79:              {
  80:                  case "name_desc":
  81:                      students = students.OrderByDescending(s => s.LastName);
  82:                      break;
  83:                  case "Date":
  84:                      students = students.OrderBy(s => s.EnrollmentDate);
  85:                      break;
  86:                  case "date_desc":
  87:                      students = students.OrderByDescending(s => s.EnrollmentDate);
  88:                      break;
  89:                  default:
  90:                      students = students.OrderBy(s => s.LastName);
  91:                      break;
  92:              }
  93:              return View(await students.AsNoTracking().ToListAsync());
  94:          }
  95:  #endregion
  96:  #elif (SortFilterPage)
  97:  #region snippet_SortFilterPage
  98:          public async Task<IActionResult> Index(
  99:              string sortOrder,
 100:              string currentFilter,
 101:              string searchString,
 102:              int? page)
 103:          {
 104:              ViewData["CurrentSort"] = sortOrder;
 105:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
 106:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
 107:   
 108:              if (searchString != null)
 109:              {
 110:                  page = 1;
 111:              }
 112:              else
 113:              {
 114:                  searchString = currentFilter;
 115:              }
 116:   
 117:              ViewData["CurrentFilter"] = searchString;
 118:   
 119:              var students = from s in _context.Students
 120:                             select s;
 121:              if (!String.IsNullOrEmpty(searchString))
 122:              {
 123:                  students = students.Where(s => s.LastName.Contains(searchString)
 124:                                         || s.FirstMidName.Contains(searchString));
 125:              }
 126:              switch (sortOrder)
 127:              {
 128:                  case "name_desc":
 129:                      students = students.OrderByDescending(s => s.LastName);
 130:                      break;
 131:                  case "Date":
 132:                      students = students.OrderBy(s => s.EnrollmentDate);
 133:                      break;
 134:                  case "date_desc":
 135:                      students = students.OrderByDescending(s => s.EnrollmentDate);
 136:                      break;
 137:                  default:
 138:                      students = students.OrderBy(s => s.LastName);
 139:                      break;
 140:              }
 141:   
 142:              int pageSize = 3;
 143:              return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), page ?? 1, pageSize));
 144:          }
 145:  #endregion
 146:  #elif (DynamicLinq)
 147:  #region snippet_DynamicLinq
 148:          public async Task<IActionResult> Index(
 149:              string sortOrder,
 150:              string currentFilter,
 151:              string searchString,
 152:              int? page)
 153:          {
 154:              ViewData["CurrentSort"] = sortOrder;
 155:              ViewData["NameSortParm"] = 
 156:                  String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
 157:              ViewData["DateSortParm"] = 
 158:                  sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";
 159:   
 160:              if (searchString != null)
 161:              {
 162:                  page = 1;
 163:              }
 164:              else
 165:              {
 166:                  searchString = currentFilter;
 167:              }
 168:   
 169:              ViewData["CurrentFilter"] = searchString;
 170:   
 171:              var students = from s in _context.Students
 172:                             select s;
 173:              
 174:              if (!String.IsNullOrEmpty(searchString))
 175:              {
 176:                  students = students.Where(s => s.LastName.Contains(searchString)
 177:                                         || s.FirstMidName.Contains(searchString));
 178:              }
 179:   
 180:              if (string.IsNullOrEmpty(sortOrder))
 181:              {
 182:                  sortOrder = "LastName";
 183:              }
 184:   
 185:              bool descending = false;
 186:              if (sortOrder.EndsWith("_desc"))
 187:              {
 188:                  sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
 189:                  descending = true;
 190:              }
 191:   
 192:              if (descending)
 193:              {
 194:                  students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
 195:              }
 196:              else
 197:              {
 198:                  students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
 199:              }
 200:         
 201:              int pageSize = 3;
 202:              return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), 
 203:                  page ?? 1, pageSize));
 204:          }
 205:  #endregion
 206:  #endif
 207:   
 208:          // GET: Students/Details/5
 209:  #region snippet_Details
 210:          public async Task<IActionResult> Details(int? id)
 211:          {
 212:              if (id == null)
 213:              {
 214:                  return NotFound();
 215:              }
 216:   
 217:              var student = await _context.Students
 218:                  .Include(s => s.Enrollments)
 219:                      .ThenInclude(e => e.Course)
 220:                  .AsNoTracking()
 221:                  .SingleOrDefaultAsync(m => m.ID == id);
 222:   
 223:              if (student == null)
 224:              {
 225:                  return NotFound();
 226:              }
 227:   
 228:              return View(student);
 229:          }
 230:  #endregion
 231:   
 232:          // GET: Students/Create
 233:          public IActionResult Create()
 234:          {
 235:              return View();
 236:          }
 237:   
 238:          // POST: Students/Create
 239:  #region snippet_Create
 240:          [HttpPost]
 241:          [ValidateAntiForgeryToken]
 242:          public async Task<IActionResult> Create(
 243:              [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
 244:          {
 245:              try
 246:              {
 247:                  if (ModelState.IsValid)
 248:                  {
 249:                      _context.Add(student);
 250:                      await _context.SaveChangesAsync();
 251:                      return RedirectToAction(nameof(Index));
 252:                  }
 253:              }
 254:              catch (DbUpdateException /* ex */)
 255:              {
 256:                  //Log the error (uncomment ex variable name and write a log.
 257:                  ModelState.AddModelError("", "Unable to save changes. " +
 258:                      "Try again, and if the problem persists " +
 259:                      "see your system administrator.");
 260:              }
 261:              return View(student);
 262:          }
 263:  #endregion
 264:   
 265:          // GET: Students/Edit/5
 266:          public async Task<IActionResult> Edit(int? id)
 267:          {
 268:              if (id == null)
 269:              {
 270:                  return NotFound();
 271:              }
 272:   
 273:              var student = await _context.Students
 274:                  .AsNoTracking()
 275:                  .SingleOrDefaultAsync(m => m.ID == id);
 276:              if (student == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:              return View(student);
 281:          }
 282:   
 283:          // POST: Students/Edit/5
 284:  #if (CreateAndAttach)
 285:  #region snippet_CreateAndAttach
 286:          public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student)
 287:          {
 288:              if (id != student.ID)
 289:              {
 290:                  return NotFound();
 291:              }
 292:              if (ModelState.IsValid)
 293:              {
 294:                  try
 295:                  {
 296:                      _context.Update(student);
 297:                      await _context.SaveChangesAsync();
 298:                      return RedirectToAction(nameof(Index));
 299:                  }
 300:                  catch (DbUpdateException /* ex */)
 301:                  {
 302:                      //Log the error (uncomment ex variable name and write a log.)
 303:                      ModelState.AddModelError("", "Unable to save changes. " +
 304:                          "Try again, and if the problem persists, " +
 305:                          "see your system administrator.");
 306:                  }
 307:              }
 308:              return View(student);
 309:          }
 310:  #endregion
 311:  #elif (ReadFirst)
 312:  #region snippet_ReadFirst
 313:          [HttpPost, ActionName("Edit")]
 314:          [ValidateAntiForgeryToken]
 315:          public async Task<IActionResult> EditPost(int? id)
 316:          {
 317:              if (id == null)
 318:              {
 319:                  return NotFound();
 320:              }
 321:              var studentToUpdate = await _context.Students.SingleOrDefaultAsync(s => s.ID == id);
 322:              if (await TryUpdateModelAsync<Student>(
 323:                  studentToUpdate,
 324:                  "",
 325:                  s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
 326:              {
 327:                  try
 328:                  {
 329:                      await _context.SaveChangesAsync();
 330:                      return RedirectToAction(nameof(Index));
 331:                  }
 332:                  catch (DbUpdateException /* ex */)
 333:                  {
 334:                      //Log the error (uncomment ex variable name and write a log.)
 335:                      ModelState.AddModelError("", "Unable to save changes. " +
 336:                          "Try again, and if the problem persists, " +
 337:                          "see your system administrator.");
 338:                  }
 339:              }
 340:              return View(studentToUpdate);
 341:          }
 342:  #endregion
 343:  #endif
 344:   
 345:          // GET: Students/Delete/5
 346:  #region snippet_DeleteGet
 347:          public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
 348:          {
 349:              if (id == null)
 350:              {
 351:                  return NotFound();
 352:              }
 353:   
 354:              var student = await _context.Students
 355:                  .AsNoTracking()
 356:                  .SingleOrDefaultAsync(m => m.ID == id);
 357:              if (student == null)
 358:              {
 359:                  return NotFound();
 360:              }
 361:   
 362:              if (saveChangesError.GetValueOrDefault())
 363:              {
 364:                  ViewData["ErrorMessage"] =
 365:                      "Delete failed. Try again, and if the problem persists " +
 366:                      "see your system administrator.";
 367:              }
 368:   
 369:              return View(student);
 370:          }
 371:  #endregion
 372:          // POST: Students/Delete/5
 373:  #if (DeleteWithReadFirst)
 374:  #region snippet_DeleteWithReadFirst
 375:          [HttpPost, ActionName("Delete")]
 376:          [ValidateAntiForgeryToken]
 377:          public async Task<IActionResult> DeleteConfirmed(int id)
 378:          {
 379:              var student = await _context.Students
 380:                  .AsNoTracking()
 381:                  .SingleOrDefaultAsync(m => m.ID == id);
 382:              if (student == null)
 383:              {
 384:                  return RedirectToAction(nameof(Index));
 385:              }
 386:   
 387:              try
 388:              {
 389:                  _context.Students.Remove(student);
 390:                  await _context.SaveChangesAsync();
 391:                  return RedirectToAction(nameof(Index));
 392:              }
 393:              catch (DbUpdateException /* ex */)
 394:              {
 395:                  //Log the error (uncomment ex variable name and write a log.)
 396:                  return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
 397:              }
 398:          }
 399:  #endregion
 400:  #elif (DeleteWithoutReadFirst)
 401:  #region snippet_DeleteWithoutReadFirst
 402:          [HttpPost]
 403:          [ValidateAntiForgeryToken]
 404:          public async Task<IActionResult> DeleteConfirmed(int id)
 405:          {
 406:              try
 407:              {
 408:                  Student studentToDelete = new Student() { ID = id };
 409:                  _context.Entry(studentToDelete).State = EntityState.Deleted;
 410:                  await _context.SaveChangesAsync();
 411:                  return RedirectToAction(nameof(Index));
 412:              }
 413:              catch (DbUpdateException /* ex */)
 414:              {
 415:                  //Log the error (uncomment ex variable name and write a log.)
 416:                  return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
 417:              }
 418:          }
 419:  #endregion
 420:  #endif
 421:      }
 422:  }

ASP.NET dependency injection will take care of passing an instance of SchoolContext into the controller. You configured that in the Startup.cs file earlier.

The controller contains an Index action method, which displays all students in the database. The method gets a list of students from the Students entity set by reading the Students property of the database context instance:

[!code-csharpMain]

   1:  #define SortFilterPage //or ScaffoldedIndex or SortOnly or SortFilter or DynamicLinq
   2:  #define ReadFirst //or CreateAndAttach
   3:  #define DeleteWithReadFirst // or DeleteWithoutReadFirst
   4:   
   5:  using System.Linq;
   6:  using System.Threading.Tasks;
   7:  using Microsoft.AspNetCore.Mvc;
   8:  using Microsoft.AspNetCore.Mvc.Rendering;
   9:  using Microsoft.EntityFrameworkCore;
  10:  using ContosoUniversity.Data;
  11:  using ContosoUniversity.Models;
  12:  using System;
  13:  using Microsoft.Extensions.Logging;
  14:   
  15:  #region snippet_Context
  16:  namespace ContosoUniversity.Controllers
  17:  {
  18:      public class StudentsController : Controller
  19:      {
  20:          private readonly SchoolContext _context;
  21:   
  22:          public StudentsController(SchoolContext context)
  23:          {
  24:              _context = context;
  25:          }
  26:  #endregion
  27:   
  28:          // GET: Students
  29:   
  30:  #if (ScaffoldedIndex)
  31:  #region snippet_ScaffoldedIndex
  32:          public async Task<IActionResult> Index()
  33:          {
  34:              return View(await _context.Students.ToListAsync());
  35:          }
  36:  #endregion
  37:  #elif (SortOnly)
  38:  #region snippet_SortOnly
  39:          public async Task<IActionResult> Index(string sortOrder)
  40:          {
  41:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  42:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  43:              var students = from s in _context.Students
  44:                             select s;
  45:              switch (sortOrder)
  46:              {
  47:                  case "name_desc":
  48:                      students = students.OrderByDescending(s => s.LastName);
  49:                      break;
  50:                  case "Date":
  51:                      students = students.OrderBy(s => s.EnrollmentDate);
  52:                      break;
  53:                  case "date_desc":
  54:                      students = students.OrderByDescending(s => s.EnrollmentDate);
  55:                      break;
  56:                  default:
  57:                      students = students.OrderBy(s => s.LastName);
  58:                      break;
  59:              }
  60:              return View(await students.AsNoTracking().ToListAsync());
  61:          }
  62:  #endregion
  63:  #elif (SortFilter)
  64:  #region snippet_SortFilter
  65:          public async Task<IActionResult> Index(string sortOrder, string searchString)
  66:          {
  67:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  68:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  69:              ViewData["CurrentFilter"] = searchString;
  70:   
  71:              var students = from s in _context.Students
  72:                             select s;
  73:              if (!String.IsNullOrEmpty(searchString))
  74:              {
  75:                  students = students.Where(s => s.LastName.Contains(searchString)
  76:                                         || s.FirstMidName.Contains(searchString));
  77:              }
  78:              switch (sortOrder)
  79:              {
  80:                  case "name_desc":
  81:                      students = students.OrderByDescending(s => s.LastName);
  82:                      break;
  83:                  case "Date":
  84:                      students = students.OrderBy(s => s.EnrollmentDate);
  85:                      break;
  86:                  case "date_desc":
  87:                      students = students.OrderByDescending(s => s.EnrollmentDate);
  88:                      break;
  89:                  default:
  90:                      students = students.OrderBy(s => s.LastName);
  91:                      break;
  92:              }
  93:              return View(await students.AsNoTracking().ToListAsync());
  94:          }
  95:  #endregion
  96:  #elif (SortFilterPage)
  97:  #region snippet_SortFilterPage
  98:          public async Task<IActionResult> Index(
  99:              string sortOrder,
 100:              string currentFilter,
 101:              string searchString,
 102:              int? page)
 103:          {
 104:              ViewData["CurrentSort"] = sortOrder;
 105:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
 106:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
 107:   
 108:              if (searchString != null)
 109:              {
 110:                  page = 1;
 111:              }
 112:              else
 113:              {
 114:                  searchString = currentFilter;
 115:              }
 116:   
 117:              ViewData["CurrentFilter"] = searchString;
 118:   
 119:              var students = from s in _context.Students
 120:                             select s;
 121:              if (!String.IsNullOrEmpty(searchString))
 122:              {
 123:                  students = students.Where(s => s.LastName.Contains(searchString)
 124:                                         || s.FirstMidName.Contains(searchString));
 125:              }
 126:              switch (sortOrder)
 127:              {
 128:                  case "name_desc":
 129:                      students = students.OrderByDescending(s => s.LastName);
 130:                      break;
 131:                  case "Date":
 132:                      students = students.OrderBy(s => s.EnrollmentDate);
 133:                      break;
 134:                  case "date_desc":
 135:                      students = students.OrderByDescending(s => s.EnrollmentDate);
 136:                      break;
 137:                  default:
 138:                      students = students.OrderBy(s => s.LastName);
 139:                      break;
 140:              }
 141:   
 142:              int pageSize = 3;
 143:              return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), page ?? 1, pageSize));
 144:          }
 145:  #endregion
 146:  #elif (DynamicLinq)
 147:  #region snippet_DynamicLinq
 148:          public async Task<IActionResult> Index(
 149:              string sortOrder,
 150:              string currentFilter,
 151:              string searchString,
 152:              int? page)
 153:          {
 154:              ViewData["CurrentSort"] = sortOrder;
 155:              ViewData["NameSortParm"] = 
 156:                  String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
 157:              ViewData["DateSortParm"] = 
 158:                  sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";
 159:   
 160:              if (searchString != null)
 161:              {
 162:                  page = 1;
 163:              }
 164:              else
 165:              {
 166:                  searchString = currentFilter;
 167:              }
 168:   
 169:              ViewData["CurrentFilter"] = searchString;
 170:   
 171:              var students = from s in _context.Students
 172:                             select s;
 173:              
 174:              if (!String.IsNullOrEmpty(searchString))
 175:              {
 176:                  students = students.Where(s => s.LastName.Contains(searchString)
 177:                                         || s.FirstMidName.Contains(searchString));
 178:              }
 179:   
 180:              if (string.IsNullOrEmpty(sortOrder))
 181:              {
 182:                  sortOrder = "LastName";
 183:              }
 184:   
 185:              bool descending = false;
 186:              if (sortOrder.EndsWith("_desc"))
 187:              {
 188:                  sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
 189:                  descending = true;
 190:              }
 191:   
 192:              if (descending)
 193:              {
 194:                  students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
 195:              }
 196:              else
 197:              {
 198:                  students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
 199:              }
 200:         
 201:              int pageSize = 3;
 202:              return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), 
 203:                  page ?? 1, pageSize));
 204:          }
 205:  #endregion
 206:  #endif
 207:   
 208:          // GET: Students/Details/5
 209:  #region snippet_Details
 210:          public async Task<IActionResult> Details(int? id)
 211:          {
 212:              if (id == null)
 213:              {
 214:                  return NotFound();
 215:              }
 216:   
 217:              var student = await _context.Students
 218:                  .Include(s => s.Enrollments)
 219:                      .ThenInclude(e => e.Course)
 220:                  .AsNoTracking()
 221:                  .SingleOrDefaultAsync(m => m.ID == id);
 222:   
 223:              if (student == null)
 224:              {
 225:                  return NotFound();
 226:              }
 227:   
 228:              return View(student);
 229:          }
 230:  #endregion
 231:   
 232:          // GET: Students/Create
 233:          public IActionResult Create()
 234:          {
 235:              return View();
 236:          }
 237:   
 238:          // POST: Students/Create
 239:  #region snippet_Create
 240:          [HttpPost]
 241:          [ValidateAntiForgeryToken]
 242:          public async Task<IActionResult> Create(
 243:              [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
 244:          {
 245:              try
 246:              {
 247:                  if (ModelState.IsValid)
 248:                  {
 249:                      _context.Add(student);
 250:                      await _context.SaveChangesAsync();
 251:                      return RedirectToAction(nameof(Index));
 252:                  }
 253:              }
 254:              catch (DbUpdateException /* ex */)
 255:              {
 256:                  //Log the error (uncomment ex variable name and write a log.
 257:                  ModelState.AddModelError("", "Unable to save changes. " +
 258:                      "Try again, and if the problem persists " +
 259:                      "see your system administrator.");
 260:              }
 261:              return View(student);
 262:          }
 263:  #endregion
 264:   
 265:          // GET: Students/Edit/5
 266:          public async Task<IActionResult> Edit(int? id)
 267:          {
 268:              if (id == null)
 269:              {
 270:                  return NotFound();
 271:              }
 272:   
 273:              var student = await _context.Students
 274:                  .AsNoTracking()
 275:                  .SingleOrDefaultAsync(m => m.ID == id);
 276:              if (student == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:              return View(student);
 281:          }
 282:   
 283:          // POST: Students/Edit/5
 284:  #if (CreateAndAttach)
 285:  #region snippet_CreateAndAttach
 286:          public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student)
 287:          {
 288:              if (id != student.ID)
 289:              {
 290:                  return NotFound();
 291:              }
 292:              if (ModelState.IsValid)
 293:              {
 294:                  try
 295:                  {
 296:                      _context.Update(student);
 297:                      await _context.SaveChangesAsync();
 298:                      return RedirectToAction(nameof(Index));
 299:                  }
 300:                  catch (DbUpdateException /* ex */)
 301:                  {
 302:                      //Log the error (uncomment ex variable name and write a log.)
 303:                      ModelState.AddModelError("", "Unable to save changes. " +
 304:                          "Try again, and if the problem persists, " +
 305:                          "see your system administrator.");
 306:                  }
 307:              }
 308:              return View(student);
 309:          }
 310:  #endregion
 311:  #elif (ReadFirst)
 312:  #region snippet_ReadFirst
 313:          [HttpPost, ActionName("Edit")]
 314:          [ValidateAntiForgeryToken]
 315:          public async Task<IActionResult> EditPost(int? id)
 316:          {
 317:              if (id == null)
 318:              {
 319:                  return NotFound();
 320:              }
 321:              var studentToUpdate = await _context.Students.SingleOrDefaultAsync(s => s.ID == id);
 322:              if (await TryUpdateModelAsync<Student>(
 323:                  studentToUpdate,
 324:                  "",
 325:                  s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
 326:              {
 327:                  try
 328:                  {
 329:                      await _context.SaveChangesAsync();
 330:                      return RedirectToAction(nameof(Index));
 331:                  }
 332:                  catch (DbUpdateException /* ex */)
 333:                  {
 334:                      //Log the error (uncomment ex variable name and write a log.)
 335:                      ModelState.AddModelError("", "Unable to save changes. " +
 336:                          "Try again, and if the problem persists, " +
 337:                          "see your system administrator.");
 338:                  }
 339:              }
 340:              return View(studentToUpdate);
 341:          }
 342:  #endregion
 343:  #endif
 344:   
 345:          // GET: Students/Delete/5
 346:  #region snippet_DeleteGet
 347:          public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
 348:          {
 349:              if (id == null)
 350:              {
 351:                  return NotFound();
 352:              }
 353:   
 354:              var student = await _context.Students
 355:                  .AsNoTracking()
 356:                  .SingleOrDefaultAsync(m => m.ID == id);
 357:              if (student == null)
 358:              {
 359:                  return NotFound();
 360:              }
 361:   
 362:              if (saveChangesError.GetValueOrDefault())
 363:              {
 364:                  ViewData["ErrorMessage"] =
 365:                      "Delete failed. Try again, and if the problem persists " +
 366:                      "see your system administrator.";
 367:              }
 368:   
 369:              return View(student);
 370:          }
 371:  #endregion
 372:          // POST: Students/Delete/5
 373:  #if (DeleteWithReadFirst)
 374:  #region snippet_DeleteWithReadFirst
 375:          [HttpPost, ActionName("Delete")]
 376:          [ValidateAntiForgeryToken]
 377:          public async Task<IActionResult> DeleteConfirmed(int id)
 378:          {
 379:              var student = await _context.Students
 380:                  .AsNoTracking()
 381:                  .SingleOrDefaultAsync(m => m.ID == id);
 382:              if (student == null)
 383:              {
 384:                  return RedirectToAction(nameof(Index));
 385:              }
 386:   
 387:              try
 388:              {
 389:                  _context.Students.Remove(student);
 390:                  await _context.SaveChangesAsync();
 391:                  return RedirectToAction(nameof(Index));
 392:              }
 393:              catch (DbUpdateException /* ex */)
 394:              {
 395:                  //Log the error (uncomment ex variable name and write a log.)
 396:                  return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
 397:              }
 398:          }
 399:  #endregion
 400:  #elif (DeleteWithoutReadFirst)
 401:  #region snippet_DeleteWithoutReadFirst
 402:          [HttpPost]
 403:          [ValidateAntiForgeryToken]
 404:          public async Task<IActionResult> DeleteConfirmed(int id)
 405:          {
 406:              try
 407:              {
 408:                  Student studentToDelete = new Student() { ID = id };
 409:                  _context.Entry(studentToDelete).State = EntityState.Deleted;
 410:                  await _context.SaveChangesAsync();
 411:                  return RedirectToAction(nameof(Index));
 412:              }
 413:              catch (DbUpdateException /* ex */)
 414:              {
 415:                  //Log the error (uncomment ex variable name and write a log.)
 416:                  return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
 417:              }
 418:          }
 419:  #endregion
 420:  #endif
 421:      }
 422:  }

You’ll learn about the asynchronous programming elements in this code later in the tutorial.

The Views/Students/Index.cshtml view displays this list in a table:

[!code-cshtmlMain]

   1:  @model IEnumerable<ContosoUniversity.Models.Student>
   2:   
   3:  @{
   4:      ViewData["Title"] = "Index";
   5:  }
   6:   
   7:  <h2>Index</h2>
   8:   
   9:  <p>
  10:      <a asp-action="Create">Create New</a>
  11:  </p>
  12:  <table class="table">
  13:      <thead>
  14:          <tr>
  15:                  <th>
  16:                      @Html.DisplayNameFor(model => model.LastName)
  17:                  </th>
  18:                  <th>
  19:                      @Html.DisplayNameFor(model => model.FirstMidName)
  20:                  </th>
  21:                  <th>
  22:                      @Html.DisplayNameFor(model => model.EnrollmentDate)
  23:                  </th>
  24:              <th></th>
  25:          </tr>
  26:      </thead>
  27:      <tbody>
  28:  @foreach (var item in Model) {
  29:          <tr>
  30:              <td>
  31:                  @Html.DisplayFor(modelItem => item.LastName)
  32:              </td>
  33:              <td>
  34:                  @Html.DisplayFor(modelItem => item.FirstMidName)
  35:              </td>
  36:              <td>
  37:                  @Html.DisplayFor(modelItem => item.EnrollmentDate)
  38:              </td>
  39:              <td>
  40:                  <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
  41:                  <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
  42:                  <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
  43:              </td>
  44:          </tr>
  45:  }
  46:      </tbody>
  47:  </table>

Press CTRL+F5 to run the project or choose Debug > Start Without Debugging from the menu.

Click the Students tab to see the test data that the DbInitializer.Initialize method inserted. Depending on how narrow your browser window is, you’ll see the Student tab link at the top of the page or you’ll have to click the navigation icon in the upper right corner to see the link.

Contoso University home page narrow
Contoso University home page narrow
Students Index page
Students Index page

View the Database

When you started the application, the DbInitializer.Initialize method calls EnsureCreated. EF saw that there was no database and so it created one, then the remainder of the Initialize method code populated the database with data. You can use SQL Server Object Explorer (SSOX) to view the database in Visual Studio.

Close the browser.

If the SSOX window isn’t already open, select it from the View menu in Visual Studio.

In SSOX, click (localdb)> Databases, and then click the entry for the database name that is in the connection string in your appsettings.json file.

Expand the Tables node to see the tables in your database.

Tables in SSOX
Tables in SSOX

Right-click the Student table and click View Data to see the columns that were created and the rows that were inserted into the table.

Student table in SSOX
Student table in SSOX

The .mdf and .ldf database files are in the C:<yourusername> folder.

Because you’re calling EnsureCreated in the initializer method that runs on app start, you could now make a change to the Student class, delete the database, run the application again, and the database would automatically be re-created to match your change. For example, if you add an EmailAddress property to the Student class, you’ll see a new EmailAddress column in the re-created table.

Conventions

The amount of code you had to write in order for the Entity Framework to be able to create a complete database for you is minimal because of the use of conventions, or assumptions that the Entity Framework makes.

Conventional behavior can be overridden. For example, you can explicitly specify table names, as you saw earlier in this tutorial. And you can set column names and set any property as primary key or foreign key, as you’ll see in a later tutorial in this series.

Asynchronous code

Asynchronous programming is the default mode for ASP.NET Core and EF Core.

A web server has a limited number of threads available, and in high load situations all of the available threads might be in use. When that happens, the server can’t process new requests until the threads are freed up. With synchronous code, many threads may be tied up while they aren’t actually doing any work because they’re waiting for I/O to complete. With asynchronous code, when a process is waiting for I/O to complete, its thread is freed up for the server to use for processing other requests. As a result, asynchronous code enables server resources to be used more efficiently, and the server is enabled to handle more traffic without delays.

Asynchronous code does introduce a small amount of overhead at run time, but for low traffic situations the performance hit is negligible, while for high traffic situations, the potential performance improvement is substantial.

In the following code, the async keyword, Task<T> return value, await keyword, and ToListAsync method make the code execute asynchronously.

[!code-csharpMain]

   1:  #define SortFilterPage //or ScaffoldedIndex or SortOnly or SortFilter or DynamicLinq
   2:  #define ReadFirst //or CreateAndAttach
   3:  #define DeleteWithReadFirst // or DeleteWithoutReadFirst
   4:   
   5:  using System.Linq;
   6:  using System.Threading.Tasks;
   7:  using Microsoft.AspNetCore.Mvc;
   8:  using Microsoft.AspNetCore.Mvc.Rendering;
   9:  using Microsoft.EntityFrameworkCore;
  10:  using ContosoUniversity.Data;
  11:  using ContosoUniversity.Models;
  12:  using System;
  13:  using Microsoft.Extensions.Logging;
  14:   
  15:  #region snippet_Context
  16:  namespace ContosoUniversity.Controllers
  17:  {
  18:      public class StudentsController : Controller
  19:      {
  20:          private readonly SchoolContext _context;
  21:   
  22:          public StudentsController(SchoolContext context)
  23:          {
  24:              _context = context;
  25:          }
  26:  #endregion
  27:   
  28:          // GET: Students
  29:   
  30:  #if (ScaffoldedIndex)
  31:  #region snippet_ScaffoldedIndex
  32:          public async Task<IActionResult> Index()
  33:          {
  34:              return View(await _context.Students.ToListAsync());
  35:          }
  36:  #endregion
  37:  #elif (SortOnly)
  38:  #region snippet_SortOnly
  39:          public async Task<IActionResult> Index(string sortOrder)
  40:          {
  41:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  42:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  43:              var students = from s in _context.Students
  44:                             select s;
  45:              switch (sortOrder)
  46:              {
  47:                  case "name_desc":
  48:                      students = students.OrderByDescending(s => s.LastName);
  49:                      break;
  50:                  case "Date":
  51:                      students = students.OrderBy(s => s.EnrollmentDate);
  52:                      break;
  53:                  case "date_desc":
  54:                      students = students.OrderByDescending(s => s.EnrollmentDate);
  55:                      break;
  56:                  default:
  57:                      students = students.OrderBy(s => s.LastName);
  58:                      break;
  59:              }
  60:              return View(await students.AsNoTracking().ToListAsync());
  61:          }
  62:  #endregion
  63:  #elif (SortFilter)
  64:  #region snippet_SortFilter
  65:          public async Task<IActionResult> Index(string sortOrder, string searchString)
  66:          {
  67:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
  68:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
  69:              ViewData["CurrentFilter"] = searchString;
  70:   
  71:              var students = from s in _context.Students
  72:                             select s;
  73:              if (!String.IsNullOrEmpty(searchString))
  74:              {
  75:                  students = students.Where(s => s.LastName.Contains(searchString)
  76:                                         || s.FirstMidName.Contains(searchString));
  77:              }
  78:              switch (sortOrder)
  79:              {
  80:                  case "name_desc":
  81:                      students = students.OrderByDescending(s => s.LastName);
  82:                      break;
  83:                  case "Date":
  84:                      students = students.OrderBy(s => s.EnrollmentDate);
  85:                      break;
  86:                  case "date_desc":
  87:                      students = students.OrderByDescending(s => s.EnrollmentDate);
  88:                      break;
  89:                  default:
  90:                      students = students.OrderBy(s => s.LastName);
  91:                      break;
  92:              }
  93:              return View(await students.AsNoTracking().ToListAsync());
  94:          }
  95:  #endregion
  96:  #elif (SortFilterPage)
  97:  #region snippet_SortFilterPage
  98:          public async Task<IActionResult> Index(
  99:              string sortOrder,
 100:              string currentFilter,
 101:              string searchString,
 102:              int? page)
 103:          {
 104:              ViewData["CurrentSort"] = sortOrder;
 105:              ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
 106:              ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
 107:   
 108:              if (searchString != null)
 109:              {
 110:                  page = 1;
 111:              }
 112:              else
 113:              {
 114:                  searchString = currentFilter;
 115:              }
 116:   
 117:              ViewData["CurrentFilter"] = searchString;
 118:   
 119:              var students = from s in _context.Students
 120:                             select s;
 121:              if (!String.IsNullOrEmpty(searchString))
 122:              {
 123:                  students = students.Where(s => s.LastName.Contains(searchString)
 124:                                         || s.FirstMidName.Contains(searchString));
 125:              }
 126:              switch (sortOrder)
 127:              {
 128:                  case "name_desc":
 129:                      students = students.OrderByDescending(s => s.LastName);
 130:                      break;
 131:                  case "Date":
 132:                      students = students.OrderBy(s => s.EnrollmentDate);
 133:                      break;
 134:                  case "date_desc":
 135:                      students = students.OrderByDescending(s => s.EnrollmentDate);
 136:                      break;
 137:                  default:
 138:                      students = students.OrderBy(s => s.LastName);
 139:                      break;
 140:              }
 141:   
 142:              int pageSize = 3;
 143:              return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), page ?? 1, pageSize));
 144:          }
 145:  #endregion
 146:  #elif (DynamicLinq)
 147:  #region snippet_DynamicLinq
 148:          public async Task<IActionResult> Index(
 149:              string sortOrder,
 150:              string currentFilter,
 151:              string searchString,
 152:              int? page)
 153:          {
 154:              ViewData["CurrentSort"] = sortOrder;
 155:              ViewData["NameSortParm"] = 
 156:                  String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
 157:              ViewData["DateSortParm"] = 
 158:                  sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";
 159:   
 160:              if (searchString != null)
 161:              {
 162:                  page = 1;
 163:              }
 164:              else
 165:              {
 166:                  searchString = currentFilter;
 167:              }
 168:   
 169:              ViewData["CurrentFilter"] = searchString;
 170:   
 171:              var students = from s in _context.Students
 172:                             select s;
 173:              
 174:              if (!String.IsNullOrEmpty(searchString))
 175:              {
 176:                  students = students.Where(s => s.LastName.Contains(searchString)
 177:                                         || s.FirstMidName.Contains(searchString));
 178:              }
 179:   
 180:              if (string.IsNullOrEmpty(sortOrder))
 181:              {
 182:                  sortOrder = "LastName";
 183:              }
 184:   
 185:              bool descending = false;
 186:              if (sortOrder.EndsWith("_desc"))
 187:              {
 188:                  sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
 189:                  descending = true;
 190:              }
 191:   
 192:              if (descending)
 193:              {
 194:                  students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
 195:              }
 196:              else
 197:              {
 198:                  students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
 199:              }
 200:         
 201:              int pageSize = 3;
 202:              return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), 
 203:                  page ?? 1, pageSize));
 204:          }
 205:  #endregion
 206:  #endif
 207:   
 208:          // GET: Students/Details/5
 209:  #region snippet_Details
 210:          public async Task<IActionResult> Details(int? id)
 211:          {
 212:              if (id == null)
 213:              {
 214:                  return NotFound();
 215:              }
 216:   
 217:              var student = await _context.Students
 218:                  .Include(s => s.Enrollments)
 219:                      .ThenInclude(e => e.Course)
 220:                  .AsNoTracking()
 221:                  .SingleOrDefaultAsync(m => m.ID == id);
 222:   
 223:              if (student == null)
 224:              {
 225:                  return NotFound();
 226:              }
 227:   
 228:              return View(student);
 229:          }
 230:  #endregion
 231:   
 232:          // GET: Students/Create
 233:          public IActionResult Create()
 234:          {
 235:              return View();
 236:          }
 237:   
 238:          // POST: Students/Create
 239:  #region snippet_Create
 240:          [HttpPost]
 241:          [ValidateAntiForgeryToken]
 242:          public async Task<IActionResult> Create(
 243:              [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
 244:          {
 245:              try
 246:              {
 247:                  if (ModelState.IsValid)
 248:                  {
 249:                      _context.Add(student);
 250:                      await _context.SaveChangesAsync();
 251:                      return RedirectToAction(nameof(Index));
 252:                  }
 253:              }
 254:              catch (DbUpdateException /* ex */)
 255:              {
 256:                  //Log the error (uncomment ex variable name and write a log.
 257:                  ModelState.AddModelError("", "Unable to save changes. " +
 258:                      "Try again, and if the problem persists " +
 259:                      "see your system administrator.");
 260:              }
 261:              return View(student);
 262:          }
 263:  #endregion
 264:   
 265:          // GET: Students/Edit/5
 266:          public async Task<IActionResult> Edit(int? id)
 267:          {
 268:              if (id == null)
 269:              {
 270:                  return NotFound();
 271:              }
 272:   
 273:              var student = await _context.Students
 274:                  .AsNoTracking()
 275:                  .SingleOrDefaultAsync(m => m.ID == id);
 276:              if (student == null)
 277:              {
 278:                  return NotFound();
 279:              }
 280:              return View(student);
 281:          }
 282:   
 283:          // POST: Students/Edit/5
 284:  #if (CreateAndAttach)
 285:  #region snippet_CreateAndAttach
 286:          public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student)
 287:          {
 288:              if (id != student.ID)
 289:              {
 290:                  return NotFound();
 291:              }
 292:              if (ModelState.IsValid)
 293:              {
 294:                  try
 295:                  {
 296:                      _context.Update(student);
 297:                      await _context.SaveChangesAsync();
 298:                      return RedirectToAction(nameof(Index));
 299:                  }
 300:                  catch (DbUpdateException /* ex */)
 301:                  {
 302:                      //Log the error (uncomment ex variable name and write a log.)
 303:                      ModelState.AddModelError("", "Unable to save changes. " +
 304:                          "Try again, and if the problem persists, " +
 305:                          "see your system administrator.");
 306:                  }
 307:              }
 308:              return View(student);
 309:          }
 310:  #endregion
 311:  #elif (ReadFirst)
 312:  #region snippet_ReadFirst
 313:          [HttpPost, ActionName("Edit")]
 314:          [ValidateAntiForgeryToken]
 315:          public async Task<IActionResult> EditPost(int? id)
 316:          {
 317:              if (id == null)
 318:              {
 319:                  return NotFound();
 320:              }
 321:              var studentToUpdate = await _context.Students.SingleOrDefaultAsync(s => s.ID == id);
 322:              if (await TryUpdateModelAsync<Student>(
 323:                  studentToUpdate,
 324:                  "",
 325:                  s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
 326:              {
 327:                  try
 328:                  {
 329:                      await _context.SaveChangesAsync();
 330:                      return RedirectToAction(nameof(Index));
 331:                  }
 332:                  catch (DbUpdateException /* ex */)
 333:                  {
 334:                      //Log the error (uncomment ex variable name and write a log.)
 335:                      ModelState.AddModelError("", "Unable to save changes. " +
 336:                          "Try again, and if the problem persists, " +
 337:                          "see your system administrator.");
 338:                  }
 339:              }
 340:              return View(studentToUpdate);
 341:          }
 342:  #endregion
 343:  #endif
 344:   
 345:          // GET: Students/Delete/5
 346:  #region snippet_DeleteGet
 347:          public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
 348:          {
 349:              if (id == null)
 350:              {
 351:                  return NotFound();
 352:              }
 353:   
 354:              var student = await _context.Students
 355:                  .AsNoTracking()
 356:                  .SingleOrDefaultAsync(m => m.ID == id);
 357:              if (student == null)
 358:              {
 359:                  return NotFound();
 360:              }
 361:   
 362:              if (saveChangesError.GetValueOrDefault())
 363:              {
 364:                  ViewData["ErrorMessage"] =
 365:                      "Delete failed. Try again, and if the problem persists " +
 366:                      "see your system administrator.";
 367:              }
 368:   
 369:              return View(student);
 370:          }
 371:  #endregion
 372:          // POST: Students/Delete/5
 373:  #if (DeleteWithReadFirst)
 374:  #region snippet_DeleteWithReadFirst
 375:          [HttpPost, ActionName("Delete")]
 376:          [ValidateAntiForgeryToken]
 377:          public async Task<IActionResult> DeleteConfirmed(int id)
 378:          {
 379:              var student = await _context.Students
 380:                  .AsNoTracking()
 381:                  .SingleOrDefaultAsync(m => m.ID == id);
 382:              if (student == null)
 383:              {
 384:                  return RedirectToAction(nameof(Index));
 385:              }
 386:   
 387:              try
 388:              {
 389:                  _context.Students.Remove(student);
 390:                  await _context.SaveChangesAsync();
 391:                  return RedirectToAction(nameof(Index));
 392:              }
 393:              catch (DbUpdateException /* ex */)
 394:              {
 395:                  //Log the error (uncomment ex variable name and write a log.)
 396:                  return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
 397:              }
 398:          }
 399:  #endregion
 400:  #elif (DeleteWithoutReadFirst)
 401:  #region snippet_DeleteWithoutReadFirst
 402:          [HttpPost]
 403:          [ValidateAntiForgeryToken]
 404:          public async Task<IActionResult> DeleteConfirmed(int id)
 405:          {
 406:              try
 407:              {
 408:                  Student studentToDelete = new Student() { ID = id };
 409:                  _context.Entry(studentToDelete).State = EntityState.Deleted;
 410:                  await _context.SaveChangesAsync();
 411:                  return RedirectToAction(nameof(Index));
 412:              }
 413:              catch (DbUpdateException /* ex */)
 414:              {
 415:                  //Log the error (uncomment ex variable name and write a log.)
 416:                  return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
 417:              }
 418:          }
 419:  #endregion
 420:  #endif
 421:      }
 422:  }

Some things to be aware of when you are writing asynchronous code that uses the Entity Framework:

For more information about asynchronous programming in .NET, see Async Overview.

Summary

You’ve now created a simple application that uses the Entity Framework Core and SQL Server Express LocalDB to store and display data. In the following tutorial, you’ll learn how to perform basic CRUD (create, read, update, delete) operations.

Next





Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19>  <20>  <21>  <22>  <23
Link to this page: //www.vb-net.com/AspNet-DocAndSamples-2017/aspnetcore/data/ef-mvc/intro.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <CHAT ME>  <ABOUT ME>  < THANKS ME>