教學課程:開始使用 ASP.NET MVC web 應用程式中的 EF CoreTutorial: Get started with EF Core in an ASP.NET MVC web app

作者:Tom DykstraRick AndersonBy Tom Dykstra and Rick Anderson

本教學課程可讓您了解具有控制器和檢視的 ASP.NET Core MVC 和 Entity Framework Core。This tutorial teaches ASP.NET Core MVC and Entity Framework Core with controllers and views. Razor 頁面是替代的程式設計模型。Razor Pages is an alternative programming model. 針對新的開發,我們建議搭配 Razor 控制器和流覽的 MVC 使用頁面。For new development, we recommend Razor Pages over MVC with controllers and views. 請參閱本教學課程的 Razor 頁面版本。See the Razor Pages version of this tutorial. 每個教學課程都涵蓋其他教學課程為涵蓋的一些內容:Each tutorial covers some material the other doesn't:

本 MVC 教學課程中的一些事項, Razor 頁面教學課程不會:Some things this MVC tutorial has that the Razor Pages tutorial doesn't:

  • 在資料模型中實作繼承Implement inheritance in the data model
  • 執行原始 SQL 查詢Perform raw SQL queries
  • 使用動態 LINQ 來簡化程式碼Use dynamic LINQ to simplify code

Razor頁面教學課程有一些東西不是:Some things the Razor Pages tutorial has that this one doesn't:

  • 使用 Select 方法來載入相關資料Use Select method to load related data
  • EF 的最佳作法。Best practices for EF.

Contoso 大學範例 web 應用程式示範如何使用 Entity Framework (EF) Core 和 Visual Studio 建立 ASP.NET Core MVC web 應用程式。The Contoso University sample web app demonstrates how to create an ASP.NET Core MVC web app using Entity Framework (EF) Core and Visual Studio.

這個範例應用程式是虛構的 Contoso 大學網站。The sample app is a web site for a fictional Contoso University. 其中包括的功能有學生入學許可、課程建立、教師指派。It includes functionality such as student admission, course creation, and instructor assignments. 這是一系列教學課程中的第一篇,說明如何建立 Contoso 大學範例應用程式。This is the first in a series of tutorials that explain how to build the Contoso University sample app.

必要條件Prerequisites

  • 如果您是 ASP.NET Core MVC 的新手,請先完成 ASP.NET CORE mvc 教學課程系列的「開始使用」,再開始這一系列。If you're new to ASP.NET Core MVC, go through the Get started with ASP.NET Core MVC tutorial series before starting this one.

資料庫引擎Database engines

Visual Studio 說明會使用 SQL Server LocalDB,它是一種只在 Windows 上執行的 SQL Server Express 版本。The Visual Studio instructions use SQL Server LocalDB, a version of SQL Server Express that runs only on Windows.

解決問題和疑難排解Solve problems and troubleshoot

如果您執行您不能解決問題,您可以藉由比較您的程式碼通常找到方案已完成的專案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. 如果您找不到您需要那里,您可以張貼問題的 StackOverflow.com ASP.NET CoreEF CoreIf you don't find what you need there, you can post a question to StackOverflow.com for ASP.NET Core or EF Core.

提示

這是 10 個教學的系列課程,當中的每一個課程都是建置於先前教學課程的成果上。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.

Contoso 大學 Web 應用程式Contoso University web app

在教學課程中建立的應用程式,是一個基本的大學網站。The app built in these tutorials is a basic university web site.

使用者可以檢視和更新學生、課程和教師資訊。Users can view and update student, course, and instructor information. 以下是應用程式中的幾個畫面:Here are a few of the screens in the app:

Students [索引] 頁面

Students [編輯] 頁面

建立 Web 應用程式Create web app

  1. 啟動 Visual Studio,然後選取 [建立新專案]。Start Visual Studio and select Create a new project.
  2. 在 [ 建立新專案 ] 對話方塊中,選取 [ ASP.NET Core Web 應用程式 > ]In the Create a new project dialog, select ASP.NET Core Web Application > Next.
  3. 在 [ 設定您的新專案 ] 對話方塊中,輸入 [ ContosoUniversity 專案名稱]。In the Configure your new project dialog, enter ContosoUniversity for Project name. 請務必使用此完整名稱(包括大小寫),以便 namespace 在複製程式碼時使用每個相符專案。It's important to use this exact name including capitalization, so each namespace matches when code is copied.
  4. 選取 [建立] 。Select Create.
  5. 在 [ 建立新的 ASP.NET Core web 應用程式 ] 對話方塊中,選取:In the Create a new ASP.NET Core web application dialog, select:
    1. 下拉式清單中的 .Net CoreASP.NET Core 5.0.NET Core and ASP.NET Core 5.0 in the dropdowns.
    2. ASP.NET Core Web 應用程式 (模型-視圖控制器)ASP.NET Core Web App (Model-View-Controller).
    3. 建立 ![新增 ASP.NET Core 專案對話方塊](~/data/ef-mvc/intro/_static/new-aspnet5.png)**Create**

New ASP.NET Core Project dialog

設定網站樣式Set up the site style

有幾個基本變更會設定網站功能表、版面配置和首頁。A few basic changes set up the site menu, layout, and home page.

開啟 Views/Shared/_Layout.cshtml 並進行下列變更:Open Views/Shared/_Layout.cshtml and make the following changes:

  • 將每個出現的變更 ContosoUniversityContoso UniversityChange each occurrence of ContosoUniversity to Contoso University. 共有三個發生次數。There are three occurrences.
  • AboutStudentsCoursesInstructorsDepartments 新增功能表項目,並刪除 Privacy 功能表項目。Add menu entries for About, Students, Courses, Instructors, and Departments, and delete the Privacy menu entry.

上述變更會在下列程式碼中反白顯示:The preceding changes are highlighted in the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>
    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

Views/Home/Index. cshtml 中,以下列標記取代檔案的內容:In Views/Home/Index.cshtml, replace the contents of the file with the following markup:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/5cu-final">See project source code &raquo;</a></p>
    </div>
</div>

按 CTRL+F5 來執行專案,或從功能表選擇 [偵錯] > [啟動但不偵錯]。Press CTRL+F5 to run the project or choose Debug > Start Without Debugging from the menu. 首頁會顯示在本教學課程中建立之頁面的索引標籤。The home page is displayed with tabs for the pages created in this tutorial.

Contoso 大學首頁

EF Core NuGet 套件EF Core NuGet packages

本教學課程使用 SQL Server,其提供者套件為 Microsoft.EntityFrameworkCore.SqlServerThis tutorial uses SQL Server, and the provider package is Microsoft.EntityFrameworkCore.SqlServer.

EF SQL Server 套件及其相依性, Microsoft.EntityFrameworkCore 以及 Microsoft.EntityFrameworkCore.Relational 提供 ef 的執行時間支援。The EF SQL Server package and its dependencies, Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.Relational, provide runtime support for EF.

新增 AspNetCore Microsoft.entityframeworkcore NuGet 套件。Add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package. 在封裝管理員主控台中 (PMC) 上,輸入下列命令以新增 NuGet 套件:In the Package Manager Console (PMC), enter the following commands to add the NuGet packages:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer

Microsoft.AspNetCore.Diagnostics.EntityFrameworkCoreNuGet 套件提供 EF Core 錯誤頁面 ASP.NET Core 中介軟體。The Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package provides ASP.NET Core middleware for EF Core error pages. 此中介軟體有助於偵測並診斷 EF Core 遷移的錯誤。This middleware helps to detect and diagnose errors with EF Core migrations.

如需其他可用於 EF Core 之資料庫提供者的詳細資訊,請參閱 資料庫提供者For information about other database providers that are available for EF Core, see Database providers.

建立資料模型Create the data model

系統會為此應用程式建立下列實體類別:The following entity classes are created for this app:

Course-Enrollment-Student 資料模型圖表

上述實體具有下列關聯性:The preceding entities have the following relationships:

  • 與實體之間有一對多關聯性 Student EnrollmentA one-to-many relationship between Student and Enrollment entities. 學生可以在任意數目的課程中註冊。A student can be enrolled in any number of courses.
  • 與實體之間有一對多關聯性 Course EnrollmentA one-to-many relationship between Course and Enrollment entities. 一堂課程可以讓任意數目的學生註冊。A course can have any number of students enrolled in it.

在下列各節中,會為每個實體建立類別。In the following sections, a class is created for each of these entities.

Student 實體The Student entity

Student 實體圖表

在 [ 模型 ] 資料夾中, Student 使用下列程式碼建立類別:In the Models folder, create the Student class with the following code:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

ID屬性(property)是對應至這個類別的資料庫資料表中, (PK) 資料行的主鍵。The ID property is the primary key (PK) column of the database table that corresponds to this class. 根據預設,EF 會將名為或的 ID 屬性 classnameID 視為主要索引鍵。By default, EF interprets a property that's named ID or classnameID as the primary key. 例如,PK 可以命名為, StudentID 而不是 IDFor example, the PK could be named StudentID rather than ID.

Enrollments 屬性為導覽屬性The Enrollments property is a navigation property. 導覽屬性會保留與此實體相關的其他實體。Navigation properties hold other entities that are related to this entity. Enrollments實體的屬性 StudentThe Enrollments property of a Student entity:

  • 包含與 Enrollment 該實體相關的所有實體 StudentContains all of the Enrollment entities that are related to that Student entity.
  • 如果 Student 資料庫中的特定資料列有兩個相關的資料 Enrollment 列:If a specific Student row in the database has two related Enrollment rows:
    • Student 實體的 Enrollments 導覽屬性包含這兩個 Enrollment 實體。That Student entity's Enrollments navigation property contains those two Enrollment entities.

Enrollment 資料列會在 StudentID 外鍵 (FK) 資料行中包含學生的 PK 值。Enrollment rows contain a student's PK value in the StudentID foreign key (FK) column.

如果導覽屬性可以保存多個實體:If a navigation property can hold multiple entities:

  • 型別必須是清單,例如 ICollection<T>List<T>HashSet<T>The type must be a list, such as ICollection<T>, List<T>, or HashSet<T>.
  • 您可以新增、刪除和更新實體。Entities can be added, deleted, and updated.

多對多和一對多導覽關聯性可包含多個實體。Many-to-many and one-to-many navigation relationships can contain multiple entities. ICollection<T>使用時,EF 預設會建立 HashSet<T> 集合。When ICollection<T> is used, EF creates a HashSet<T> collection by default.

Enrollment 實體The Enrollment entity

Enrollment 實體圖表

在 [ 模型 ] 資料夾中, Enrollment 使用下列程式碼建立類別:In the Models folder, create the Enrollment class with the following code:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

EnrollmentID屬性是 PK。The EnrollmentID property is the PK. 這個實體會使用 classnameID 模式,而不是 ID 本身。This entity uses the classnameID pattern instead of ID by itself. Student使用模式的實體 IDThe Student entity used the ID pattern. 有些開發人員偏好在資料模型中使用一種模式。Some developers prefer to use one pattern throughout the data model. 在本教學課程中,變化說明可以使用任一種模式。In this tutorial, the variation illustrates that either pattern can be used. 稍後的教學課程會示範如何使用 ID 不含 classname,讓您更輕鬆地在資料模型中執行繼承。A later tutorial shows how using ID without classname makes it easier to implement inheritance in the data model.

Grade 屬性為一個 enumThe Grade property is an enum. 型別宣告 ? 之後的 Grade 會指出該 Grade 屬性可 為 nullThe ? after the Grade type declaration indicates that the Grade property is nullable. 其等級與 null 零級不同。A grade that's null is different from a zero grade. null 表示不知道或尚未指派等級。null means a grade isn't known or hasn't been assigned yet.

StudentID屬性是 (FK) 的外鍵,而對應的導覽屬性為 StudentThe StudentID property is a foreign key (FK), and the corresponding navigation property is Student. Enrollment實體與一個實體相關聯 Student ,因此屬性只能包含單一 Student 實體。An Enrollment entity is associated with one Student entity, so the property can only hold a single Student entity. 這不同于 Student.Enrollments 可保存多個實體的導覽屬性 EnrollmentThis differs from the Student.Enrollments navigation property, which can hold multiple Enrollment entities.

CourseID屬性是 FK,而對應的導覽屬性為 CourseThe CourseID property is a FK, and the corresponding navigation property is Course. 一個 Enrollment 實體與一個 Course 實體建立關聯。An Enrollment entity is associated with one Course entity.

Entity Framework 會將屬性(property)解讀為 FK 屬性(property),並將其命名為「 < 導覽屬性名稱 >< 主鍵屬性名稱」 >Entity Framework interprets a property as a FK property if it's named <navigation property name><primary key property name>. 例如, StudentID Student 因為 Student 實體的 PK 是,所以針對導覽屬性 IDFor example, StudentID for the Student navigation property since the Student entity's PK is ID. FK 屬性也可以命名 < 為主鍵屬性名稱 >FK properties can also be named <primary key property name>. 例如, CourseID 因為 Course 實體的 PK 為 CourseIDFor example, CourseID because the Course entity's PK is CourseID.

Course 實體The Course entity

Course 實體圖表

在 [ 模型 ] 資料夾中, Course 使用下列程式碼建立類別:In the Models folder, create the Course class with the following code:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Enrollments 屬性為導覽屬性。The Enrollments property is a navigation property. Course 實體可以與任何數量的 Enrollment 實體相關。A Course entity can be related to any number of Enrollment entities.

DatabaseGenerated屬性將在稍後的教學課程中說明。The DatabaseGenerated attribute is explained in a later tutorial. 這個屬性可讓您在課程中輸入 PK,而非讓資料庫產生它。This attribute allows entering the PK for the course rather than having the database generate it.

建立資料庫內容Create the database context

協調指定資料模型之 EF 功能的主要類別是 DbContext 資料庫內容類別。The main class that coordinates EF functionality for a given data model is the DbContext database context class. 此類別是透過衍生自 Microsoft.EntityFrameworkCore.DbContext 類別來建立。This class is created by deriving from the Microsoft.EntityFrameworkCore.DbContext class. DbContext衍生類別會指定要包含在資料模型中的實體。The DbContext derived class specifies which entities are included in the data model. 某些 EF 行為可以自訂。Some EF behaviors can be customized. 在此專案中,類別命名為 SchoolContextIn this project, the class is named SchoolContext.

在專案資料夾中,建立名為的資料夾 DataIn the project folder, create a folder named Data.

在 [ Data ] 資料夾中, SchoolContext 使用下列程式碼建立類別:In the Data folder create a SchoolContext class with the following code:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
    }
}

上述程式碼會 DbSet 為每個實體集建立屬性。The preceding code creates a DbSet property for each entity set. EF 術語:In EF terminology:

  • 實體集通常會對應到資料庫資料表。An entity set typically corresponds to a database table.
  • 實體會對應至資料表中的資料列。An entity corresponds to a row in the table.

DbSet<Enrollment> DbSet<Course> 可以省略和語句,其運作方式也相同。The DbSet<Enrollment> and DbSet<Course> statements could be omitted and it would work the same. EF 會隱含地包含它們,因為:EF would include them implicitly because:

  • Student實體參考 Enrollment 實體。The Student entity references the Enrollment entity.
  • Enrollment實體參考 Course 實體。The Enrollment entity references the Course entity.

資料庫建立時,EF 會建立和 DbSet 屬性名稱相同的資料表。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 而不是 StudentFor example, Students rather than Student. 針對是否要複數化資料表名稱,開發人員並沒有共識。Developers disagree about whether table names should be pluralized or not. 在這些教學課程中,會藉由在中指定單一資料表名稱來覆寫預設行為 DbContextFor these tutorials, the default behavior is overridden by specifying singular table names in the DbContext. 若要完成這項操作,請在最後一個 DbSet 屬性後方新增下列醒目提示程式碼。To do that, add the following highlighted code after the last DbSet property.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

註冊 SchoolContextRegister the SchoolContext

ASP.NET Core 包含了相依性插入ASP.NET Core includes dependency injection. 服務(例如 EF 資料庫內容)會在應用程式啟動期間以相依性插入來註冊。Services, such as the EF database context, are registered with dependency injection during app startup. 需要這些服務的元件(例如 MVC 控制器)是透過函式參數提供這些服務。Components that require these services, such as MVC controllers, are provided these services via constructor parameters. 本教學課程稍後會顯示取得內容實例的控制器程式碼。The controller constructor code that gets a context instance is shown later in this tutorial.

若要將 SchoolContext 註冊為服務,請開啟 Startup.cs,並將醒目標示的程式碼新增至 ConfigureServices 方法。To register SchoolContext as a service, open Startup.cs, and add the highlighted lines to the ConfigureServices method.

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ContosoUniversity
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SchoolContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddControllersWithViews();
        }

連接字串的名稱,會透過呼叫 DbContextOptionsBuilder 物件上的方法來傳遞至內容。The name of the connection string is passed in to the context by calling a method on a DbContextOptionsBuilder object. 針對本機開發, ASP.NET Core 設定系統 會從檔案讀取連接字串 appsettings.jsonFor local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

開啟檔案 appsettings.json ,並加入連接字串,如下列標記所示:Open the appsettings.json file and add a connection string as shown in the following markup:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

新增資料庫例外狀況篩選準則Add the database exception filter

將加入 AddDatabaseDeveloperPageExceptionFilter 至, ConfigureServices 如下列程式碼所示:Add AddDatabaseDeveloperPageExceptionFilter to ConfigureServices as shown in the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();

    services.AddControllersWithViews();
}

AddDatabaseDeveloperPageExceptionFilter開發環境中提供有用的錯誤資訊。The AddDatabaseDeveloperPageExceptionFilter provides helpful error information in the development environment.

SQL Server Express LocalDBSQL Server Express LocalDB

連接字串會指定 SQL Server LocalDBThe connection string specifies SQL Server LocalDB. LocalDB 是輕量版的 SQL Server Express Database Engine,旨在用於應用程序開發,而不是生產用途。LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. LocalDB 會依需求啟動,並以使用者模式執行,因此沒有複雜的組態。LocalDB starts on demand and runs in user mode, so there's no complex configuration. LocalDB 預設會在 C:/Users/<user> 目錄中建立 .mdf 資料庫檔案。By default, LocalDB creates .mdf DB files in the C:/Users/<user> directory.

使用測試資料將 DB 初始化Initialize DB with test data

EF 會建立空的資料庫。EF creates an empty database. 在本節中,會加入在建立資料庫之後呼叫的方法,以便將測試資料填入。In this section, a method is added that's called after the database is created in order to populate it with test data.

EnsureCreated方法是用來自動建立資料庫。The EnsureCreated method is used to automatically create the database. 稍後的教學課程中,您將瞭解如何使用 Code First 移轉來變更資料庫架構,而不是卸載和重新建立資料庫,來處理模型變更。In a later tutorial, you see how to handle model changes by using Code First Migrations to change the database schema instead of dropping and re-creating the database.

在 [ 資料 ] 資料夾中,使用下列程式碼來建立名為的新類別 DbInitializerIn the Data folder, create a new class named DbInitializer with the following code:

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

上述程式碼會檢查資料庫是否存在:The preceding code checks if the database exists:

  • 如果找不到資料庫,則為,If the database is not found;
    • 它會建立並載入測試資料。It is created and loaded with test data. 會將測試資料載入至陣列而非 List<T> 集合,以最佳化效能。It loads test data into arrays rather than List<T> collections to optimize performance.
  • 如果找到資料庫,則不會採取任何動作。If the database if found, it takes no action.

使用下列程式碼更新 程式 .csUpdate Program.cs with the following code:

using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

.Cs 會在應用程式啟動時執行下列動作:Program.cs does the following on app startup:

  • 從相依性插入容器中取得資料庫內容執行個體。Get a database context instance from the dependency injection container.
  • 呼叫 DbInitializer.Initialize 方法。Call the DbInitializer.Initialize method.
  • 當方法完成時處置內容, Initialize 如下列程式碼所示:Dispose the context when the Initialize method completes as shown in the following code:
public static void Main(string[] args)
{
     var host = CreateWebHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<SchoolContext>();
            DbInitializer.Initialize(context);
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred while seeding the database.");
        }
    }

    host.Run();
}

第一次執行應用程式時,會建立資料庫並載入測試資料。The first time the app is run, the database is created and loaded with test data. 每次資料模型變更時:Whenever the data model changes:

  • 刪除資料庫。Delete the database.
  • 更新種子方法,並以新的資料庫開始再次。Update the seed method, and start afresh with a new database.

在稍後的教學課程中,資料庫會在資料模型變更時進行修改,而不需要刪除然後再重新建立。In later tutorials, the database is modified when the data model changes, without deleting and re-creating it. 當資料模型變更時,不會遺失任何資料。No data is lost when the data model changes.

建立控制器和檢視Create controller and views

使用 Visual Studio 中的「樣板」引擎來新增 MVC 控制器和將使用 EF 來查詢和儲存資料的視圖。Use the scaffolding engine in Visual Studio to add an MVC controller and views that will use EF to query and save data.

自動建立 CRUD 動作方法和視圖稱為「樣板」。The automatic creation of CRUD action methods and views is known as scaffolding.

  • 方案總管 中,以滑鼠右鍵按一下 Controllers 資料夾,然後選取 [ 加入 > 新的 scaffold 專案]。In Solution Explorer, right-click the Controllers folder and select Add > New Scaffolded Item.
  • 在 [新增 Scaffold] 對話方塊中:In the Add Scaffold dialog box:
    • 選取 [使用 Entity Framework 執行檢視的 MVC 控制器]。Select MVC controller with views, using Entity Framework.
    • 按一下 [新增]Click Add. [ Entity Framework 使用 Scaffold 新增 MVC 控制器 ] 對話方塊隨即出現:  StudentThe Add MVC Controller with views, using Entity Framework dialog box appears: Scaffold Student
    • 在 [ 模型類別] 中選取 [ Student]。In Model class, select Student.
    • 在 [ 資料內容類別] 中,選取 [ SchoolCoNtext]。In Data context class, select SchoolContext.
    • 接受預設的 StudentsController 作為名稱。Accept the default StudentsController as the name.
    • 按一下 [新增]Click Add.

Visual Studio 的樣板引擎會建立一個檔案 StudentsController.cs ,以及一組 *.cshtml 與控制器一起使用的 (檔案) 。The Visual Studio scaffolding engine creates a StudentsController.cs file and a set of views (*.cshtml files) that work with the controller.

請注意,控制器會採用作為函式 SchoolContext 參數。Notice the controller takes a SchoolContext as a constructor parameter.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

ASP.NET Core 相依性插入會負責傳遞 SchoolContext 的執行個體給控制器。ASP.NET Core dependency injection takes care of passing an instance of SchoolContext into the controller. 您已在類別中進行設定 StartupYou configured that in the Startup class.

控制器含有一個 Index 動作方法,該方法會顯示資料庫中的所有學生。The controller contains an Index action method, which displays all students in the database. 方法會藉由讀取資料庫內容執行個體的 Students 屬性,來從 Students 實體集中取得學生的清單:The method gets a list of students from the Students entity set by reading the Students property of the database context instance:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

本教學課程稍後會說明此程式碼中的非同步程式設計項目。The asynchronous programming elements in this code are explained later in the tutorial.

Views/Students/Index.cshtml 檢視會在一個資料表中顯示這份清單:The Views/Students/Index.cshtml view displays this list in a table:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

按 CTRL+F5 來執行專案,或從功能表選擇 [偵錯] > [啟動但不偵錯]。Press CTRL+F5 to run the project or choose Debug > Start Without Debugging from the menu.

按一下 [Students] 索引標籤來查看 DbInitializer.Initialize 方法插入的測試資料。Click the Students tab to see the test data that the DbInitializer.Initialize method inserted. 取決於您瀏覽器視窗的寬度,您可能會在頁面的頂端看到 Students 索引標籤連結,或是按一下位於右上角的導覽圖示來查看連結。Depending on how narrow your browser window is, you'll see the Students 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 大學首頁 (窄)

Students [索引] 頁面

檢視資料庫View the database

當應用程式啟動時,方法就會 DbInitializer.Initialize 呼叫 EnsureCreatedWhen the app is started, the DbInitializer.Initialize method calls EnsureCreated. EF 看到沒有資料庫:EF saw that there was no database:

  • 所以它會建立一個資料庫。So it created a database.
  • Initialize方法程式碼會將資料填入資料庫。The Initialize method code populated the database with data.

使用 SQL Server 物件總管 (SSOX) 來查看 Visual Studio 中的資料庫:Use SQL Server Object Explorer (SSOX) to view the database in Visual Studio:

  • 從 Visual Studio 的 [ View ] 功能表中選取 [ SQL Server 物件總管]。Select SQL Server Object Explorer from the View menu in Visual Studio.
  • 在 SSOX 中,選取 (localdb) \mssqllocaldb > 資料庫In SSOX, select (localdb)\MSSQLLocalDB > Databases.
  • 在檔案的 ContosoUniversity1 連接字串中,選取資料庫名稱的專案 appsettings.jsonSelect ContosoUniversity1, the entry for the database name that's in the connection string in the appsettings.json file.
  • 展開 [ 資料表] 節點,查看資料庫中的資料表。Expand the Tables node to see the tables in the database.

SSOX 中的資料表

以滑鼠右鍵按一下 Student 資料表,然後按一下 [ View data ] 以查看資料表中的資料。Right-click the Student table and click View Data to see the data in the table.

SSOX 中的 Student 資料表

*.mdf和資料庫檔案位於 *.ldf C:\Users \ <username> 資料夾中。The *.mdf and *.ldf database files are in the C:\Users\<username> folder.

因為 EnsureCreated 會在應用程式啟動時執行的初始化運算式方法中呼叫,所以您可以:Because EnsureCreated is called in the initializer method that runs on app start, you could:

  • 對類別進行變更 StudentMake a change to the Student class.
  • 刪除資料庫。Delete the database.
  • 停止,然後啟動應用程式。Stop, then start the app. 系統會自動重新建立資料庫以符合變更。The database is automatically re-created to match the change.

例如,如果將某個 EmailAddress 屬性加入至 Student 類別, EmailAddress 則會在重新建立的資料表中加入新的資料行。For example, if an EmailAddress property is added to the Student class, a new EmailAddress column in the re-created table. View 不會顯示新的 EmailAddress 屬性。The view won't display the new EmailAddress property.

慣例Conventions

為了讓 EF 建立完整資料庫而撰寫的程式碼數量很少,因為使用 EF 所使用的慣例:The amount of code written in order for the EF to to create a complete database is minimal because of the use of the conventions EF uses:

  • DbSet 屬性的名稱會用於資料表名稱。The names of DbSet properties are used as table names. 針對 DbSet 屬性並未參考的實體,實體類別名稱會用於資料表名稱。For entities not referenced by a DbSet property, entity class names are used as table names.
  • 實體屬性名稱會用於資料行名稱。Entity property names are used for column names.
  • 名為或的實體屬性會被辨識 ID classnameID 為 PK 屬性。Entity properties that are named ID or classnameID are recognized as PK properties.
  • 如果屬性名稱為「 < 導覽屬性名稱 PK」屬性名稱,則會將屬性視為 FK 屬性 >< >A property is interpreted as a FK property if it's named <navigation property name><PK property name>. 例如, StudentID Student 因為 Student 實體的 PK 是,所以針對導覽屬性 IDFor example, StudentID for the Student navigation property since the Student entity's PK is ID. FK 屬性也可以命名 < 為主鍵屬性名稱 >FK properties can also be named <primary key property name>. 例如, EnrollmentID 因為 Enrollment 實體的 PK 為 EnrollmentIDFor example, EnrollmentID since the Enrollment entity's PK is EnrollmentID.

慣例行為可以被覆寫。Conventional behavior can be overridden. 例如,您可以明確指定資料表名稱,如本教學課程稍早所述。For example, table names can be explicitly specified, as shown earlier in this tutorial. 資料行名稱和任何屬性都可以設定為 PK 或 FK。Column names and any property can be set as a PK or FK.

非同步程式碼Asynchronous code

非同步程式設計是預設的 ASP.NET Core 和 EF Core 模式。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. 使用同步程式碼,許多執行緒可能在實際上並未執行任何工作時受到占用,原因是在等候 I/O 完成。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. 使用非同步程式碼,處理程序在等候 I/O 完成時,其執行緒將會空出來以讓伺服器處理其他要求。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.

在下列程式碼中,、、 async Task<T> 和會使程式 await ToListAsync 代碼以非同步方式執行。In the following code, async, Task<T>, await, and ToListAsync make the code execute asynchronously.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • async 關鍵字會告訴編譯器為方法本體的一部分產生回呼,並自動建立傳回的 Task<IActionResult> 物件。The async keyword tells the compiler to generate callbacks for parts of the method body and to automatically create the Task<IActionResult> object that's returned.
  • 傳回類型 Task<IActionResult> 代表了正在進行的工作,其結果為 IActionResult 類型。The return type Task<IActionResult> represents ongoing work with a result of type IActionResult.
  • await 關鍵字會使編譯器將方法分割為兩部分。The await keyword causes the compiler to split the method into two parts. 第一個部分會與非同步啟動的作業一同結束。The first part ends with the operation that's started asynchronously. 第二個部分則會放入作業完成時呼叫的回呼方法中。The second part is put into a callback method that's called when the operation completes.
  • ToListAsyncToList 擴充方法的非同步版本。ToListAsync is the asynchronous version of the ToList extension method.

撰寫使用 EF 的非同步程式碼時,必須注意的一些事項:Some things to be aware of when writing asynchronous code that uses EF:

  • 只有讓查詢或命令傳送至資料庫的陳述式,會以非同步方式執行。Only statements that cause queries or commands to be sent to the database are executed asynchronously. 其中包含,例如 ToListAsyncSingleOrDefaultAsync,以及 SaveChangesAsyncThat includes, for example, ToListAsync, SingleOrDefaultAsync, and SaveChangesAsync. 其中不包含,例如:僅變更 IQueryable 的陳述式,例如 var students = context.Students.Where(s => s.LastName == "Davolio")It doesn't include, for example, statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • EF 內容在執行緒中並不安全:不要嘗試執行多個平行作業。An EF context isn't thread safe: don't try to do multiple operations in parallel. 當您呼叫任何 async EF 方法時,請一律使用 await 關鍵字。When you call any async EF method, always use the await keyword.
  • 若要利用非同步程式碼的效能優勢,請確定任何使用的程式庫套件如果呼叫任何 EF 方法,而導致查詢傳送到資料庫,則請確定任何使用的程式庫套件也都使用 async。To take advantage of the performance benefits of async code, make sure that any library packages used also use async if they call any EF methods that cause queries to be sent to the database.

如需在 .NET 中非同步程式設計的詳細資訊,請參閱非同步總覽For more information about asynchronous programming in .NET, see Async Overview.

已提取的實體數目Limit entities fetched

請參閱 效能考慮 ,以取得限制查詢所傳回之數目或實體的相關資訊。See Performance considerations for information on limiting the number or entities returned from a query.

若要了解如何執行基本的 CRUD (建立、讀取、更新、刪除) 作業,請前往下一個教學課程。Advance to the next tutorial to learn how to perform basic CRUD (create, read, update, delete) operations.

本教學課程可讓您了解具有控制器和檢視的 ASP.NET Core MVC 和 Entity Framework Core。This tutorial teaches ASP.NET Core MVC and Entity Framework Core with controllers and views. Razor 頁面是替代的程式設計模型。Razor Pages is an alternative programming model. 針對新的開發,我們建議搭配 Razor 控制器和流覽的 MVC 使用頁面。For new development, we recommend Razor Pages over MVC with controllers and views. 請參閱本教學課程的 Razor 頁面版本。See the Razor Pages version of this tutorial. 每個教學課程都涵蓋其他教學課程為涵蓋的一些內容:Each tutorial covers some material the other doesn't:

本 MVC 教學課程中的一些事項, Razor 頁面教學課程不會:Some things this MVC tutorial has that the Razor Pages tutorial doesn't:

  • 在資料模型中實作繼承Implement inheritance in the data model
  • 執行原始 SQL 查詢Perform raw SQL queries
  • 使用動態 LINQ 來簡化程式碼Use dynamic LINQ to simplify code

Razor頁面教學課程有一些東西不是:Some things the Razor Pages tutorial has that this one doesn't:

  • 使用 Select 方法來載入相關資料Use Select method to load related data
  • EF 的最佳作法。Best practices for EF.

Contoso 大學範例 Web 應用程式示範如何使用 Entity Framework (EF) Core 2.2 和 Visual Studio 2017 或 2019 建立 ASP.NET Core 2.2 MVC Web 應用程式。The Contoso University sample web application demonstrates how to create ASP.NET Core 2.2 MVC web applications using Entity Framework (EF) Core 2.2 and Visual Studio 2017 or 2019.

本教學課程尚未針對 ASP.NET Core 3.1 進行更新。This tutorial has not been updated for ASP.NET Core 3.1. 它已針對 ASP.NET Core 5.0進行更新。It has been updated for ASP.NET Core 5.0.

這個範例應用程式是虛構的 Contoso 大學網站。The sample application is a web site for a fictional Contoso University. 其中包括的功能有學生入學許可、課程建立、教師指派。It includes functionality such as student admission, course creation, and instructor assignments. 這是說明如何從零開始建立 Contoso 大學範例應用程式教學課程系列中的第一頁。This is the first in a series of tutorials that explain how to build the Contoso University sample application from scratch.

必要條件Prerequisites

疑難排解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. 如果您找不到您需要那里,您可以張貼問題的 StackOverflow.com ASP.NET CoreEF CoreIf you don't find what you need there, you can post a question to StackOverflow.com for ASP.NET Core or EF Core.

提示

這是 10 個教學的系列課程,當中的每一個課程都是建置於先前教學課程的成果上。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.

Contoso 大學 Web 應用程式Contoso University web app

您在這些教學課程中會建置的應用程式為一個簡單的大學網站。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 [索引] 頁面

Students [編輯] 頁面

建立 Web 應用程式Create web app

  • 開啟 Visual Studio。Open Visual Studio.

  • 從 [檔案] 功能表選取[新增] > [專案] 。From the File menu, select New > Project.

  • 從左側窗格中,選取 [已安裝] > [Visual C#] > [Web]。From the left pane, select Installed > Visual C# > Web.

  • 選取 [ASP.NET Core Web 應用程式] 專案範本。Select the ASP.NET Core Web Application project template.

  • 輸入 ContosoUniversity 作為名稱,然後按一下 [確定]。Enter ContosoUniversity as the name and click OK.

    [新增專案] 對話方塊

  • 等候 [新增 ASP.NET Core Web 應用程式] 對話方塊出現。Wait for the New ASP.NET Core Web Application dialog to appear.

  • 選取 [.NET Core]、[ASP.NET Core 2.2] 和 [Web 應用程式 (Model-View-Controller)] 範本。Select .NET Core, ASP.NET Core 2.2 and the Web Application (Model-View-Controller) template.

  • 確定 [ 驗證 ] 設定為 [ 無驗證]。Make sure Authentication is set to No Authentication.

  • 選取 [確定]Select OK

    [新增 ASP.NET Core 專案] 對話方塊

設定網站樣式Set up the site style

一些簡單的變更會設定網站的功能表、配置和首頁。A few simple changes will set up the site menu, layout, and home page.

開啟 Views/Shared/_Layout.cshtml 並進行下列變更:Open Views/Shared/_Layout.cshtml and make the following changes:

  • 將每個出現的 "ContosoUniversity" 都變更為 "Contoso University"。Change each occurrence of "ContosoUniversity" to "Contoso University". 共有三個發生次數。There are three occurrences.

  • AboutStudentsCoursesInstructorsDepartments 新增功能表項目,並刪除 Privacy 功能表項目。Add menu entries for About, Students, Courses, Instructors, and Departments, and delete the Privacy menu entry.

所做的變更已醒目提示。The changes are highlighted.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
              crossorigin="anonymous"
              integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
    </environment>
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <partial name="_CookieConsentPartial" />
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
        </div>
    </footer>

    <environment include="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    </environment>
    <environment exclude="Development">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
        </script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha256-E/V4cWE4qvAeO5MOhjtGtqDzPndRO1LBk8lJ/PR7CA4=">
        </script>
    </environment>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

Views/Home/Index.cshtml 中用下列程式碼取代檔案內容,以使用關於此應用程式的文字來取代關於 ASP.NET 和 MVC 的文字: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:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code &raquo;</a></p>
    </div>
</div>

按 CTRL+F5 來執行專案,或從功能表選擇 [偵錯] > [啟動但不偵錯]。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 大學首頁

關於 EF Core NuGet 套件About EF Core NuGet packages

若要將 EF Core 支援新增至專案,請安裝您欲使用之資料庫的提供者。To add EF Core support to a project, install the database provider that you want to target. 本教學課程使用 SQL Server,其提供者套件為 Microsoft.EntityFrameworkCore.SqlServerThis tutorial uses SQL Server, and the provider package is Microsoft.EntityFrameworkCore.SqlServer. 此套件包含在 Microsoft.AspNetCore.App metapackage 中,因此您不需要參考該套件。This package is included in the Microsoft.AspNetCore.App metapackage, so you don't need to reference the package.

EF SQL Server 套件及其相依性 (Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.Relational) 提供了 EF 的執行階段支援。The EF SQL Server 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.

如需其他 Entity Framework Core 可用之資料庫提供者的資訊,請參閱資料庫提供者For information about other database providers that are available for Entity Framework Core, see Database providers.

建立資料模型Create the data model

接下來您會為 Contoso 大學應用程式建立實體類別。Next you'll create entity classes for the Contoso University application. 您會從下列三個實體開始。You'll start with the following three entities.

Course-Enrollment-Student 資料模型圖表

StudentEnrollment 實體之間存在一對多關聯性,CourseEnrollment 實體之間也存在一對多關聯性。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.

Student 實體The Student entity

Student 實體圖表

Models 資料夾中,建立一個名為 Student.cs 的類別檔案,然後使用下列程式碼取代範本程式碼。In the Models folder, create a class file named Student.cs and replace the template code with the following code.

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

ID 屬性會成為資料庫資料表中的主索引鍵資料行,並對應至這個類別。The ID property will become the primary key column of the database table that corresponds to this class. 根據預設,Entity Framework 會將名為 IDclassnameID 的屬性解譯為主索引鍵。By default, the Entity Framework interprets a property that's named ID or classnameID as the primary key.

Enrollments 屬性為導覽屬性The Enrollments property is a navigation property. 導覽屬性會保留與此實體相關的其他實體。Navigation properties hold other entities that are related to this entity. 在這個案例中,Student entityEnrollments 屬性會保有與該 Student 實體相關的所有 Enrollment 實體。In this case, the Enrollments property of a Student entity will hold all of the Enrollment entities that are related to that Student entity. 換句話說,如果 Student 資料庫中的資料列有兩個相關 Enrollment 的資料列 (在其 StudentID 外鍵資料行中包含該學生的主鍵值的資料列) ,該 Student 實體的 Enrollments 導覽屬性將會包含這兩個 Enrollment 實體。In other words, if a 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.

若導覽屬性可保有多個實體 (例如在多對多或一對多關聯性中的情況),其類型必須為一個清單,使得實體可以在該清單中新增、刪除或更新,例如 ICollection<T>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>. 您可以指定 ICollection<T> 或如 List<T>HashSet<T> 等類型。You can specify ICollection<T> or a type such as List<T> or HashSet<T>. 若您指定了 ICollection<T>,EF 會根據預設建立一個 HashSet<T> 集合。If you specify ICollection<T>, EF creates a HashSet<T> collection by default.

Enrollment 實體The Enrollment entity

Enrollment 實體圖表

Models 資料夾中,建立 Enrollment.cs,然後使用下列程式碼取代現有的程式碼:In the Models folder, create Enrollment.cs and replace the existing code with the following code:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

EnrollmentID 屬性將為主索引鍵。此實體會使用 classnameID 模式,而非您在 Student 實體中所見到的自身 IDThe 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. 稍後的教學課程中,您會了解使用沒有 classname 的識別碼可讓在資料模型中實作繼承變得更為簡單。In a later tutorial, you'll see how using ID without classname makes it easier to implement inheritance in the data model.

Grade 屬性為一個 enumThe Grade property is an enum. 問號之後的 Grade 類型宣告表示 Grade 屬性可為 Null。The question mark after the Grade type declaration indicates that the Grade property is nullable. 為 Null 的成績不同於成績為零:Null 表示成績未知或尚未指派。A grade that's null is different from a zero grade -- null means a grade isn't known or hasn't been assigned yet.

StudentID 屬性是外部索引鍵,對應的導覽屬性是 StudentThe StudentID property is a foreign key, and the corresponding navigation property is Student. Enrollment 實體與一個 Student 實體關聯,因此屬性僅能保有單一 Student 實體 (不像您先前看到的 Student.Enrollments 導覽屬性可保有多個 Enrollment 實體)。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).

CourseID 屬性是外部索引鍵,對應的導覽屬性是 CourseThe CourseID property is a foreign key, and the corresponding navigation property is Course. 一個 Enrollment 實體與一個 Course 實體建立關聯。An Enrollment entity is associated with one Course entity.

Entity Framework 會將名為 <navigation property name><primary key property name> 的屬性解譯為外部索引鍵屬性 (例如 Student 導覽屬性的 StudentID,因為 Student 實體的主索引鍵為 ID)。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). 外部索引鍵屬性也可以簡單的命名為 <primary key property name> (例如 CourseID,因為 Course 實體的主索引鍵為 CourseID)。Foreign key properties can also be named simply <primary key property name> (for example, CourseID since the Course entity's primary key is CourseID).

Course 實體The Course entity

Course 實體圖表

Models 資料夾中,建立 Course.cs,然後使用下列程式碼取代現有的程式碼:In the Models folder, create Course.cs and replace the existing code with the following code:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Enrollments 屬性為導覽屬性。The Enrollments property is a navigation property. Course 實體可以與任何數量的 Enrollment 實體相關。A Course entity can be related to any number of Enrollment entities.

我們會在此系列稍後的教學課程中進一步討論 DatabaseGenerated 屬性。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

為指定資料模型協調 Entity Framework 功能的主要類別便是資料庫內容類別。The main class that coordinates Entity Framework functionality for a given data model is the database context class. 若要建立此類別,您可以從 Microsoft.EntityFrameworkCore.DbContext 類別來衍生。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. 您也可以自訂某些 Entity Framework 行為。You can also customize certain Entity Framework behavior. 在此專案中,類別命名為 SchoolContextIn this project, the class is named SchoolContext.

在專案資料夾中,建立名為 Data 的資料夾。In the project folder, create a folder named Data.

Data 資料夾中,建立名為 SchoolContext.cs 的新類別檔案,然後使用下列程式碼取代範本程式碼:In the Data folder create a new class file named SchoolContext.cs, and replace the template code with the following code:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }
    }
}

程式碼會為每一個實體集建立 DbSet 屬性。This code creates a DbSet property for each entity set. 在 Entity Framework 詞彙中,實體集通常會對應至資料庫資料表,而實體則對應至資料表中的資料列。In Entity Framework terminology, an entity set typically corresponds to a database table, and an entity corresponds to a row in the table.

您可以省略 DbSet<Enrollment>DbSet<Course> 陳述式,其結果也會是相同的。You could've omitted the DbSet<Enrollment> and DbSet<Course> statements and it would work the same. Entity Framework 會隱含它們,因為 Student 實體參考了 Enrollment 實體;而 Enrollment 實體參考了 Course 實體。The Entity Framework would include them implicitly because the Student entity references the Enrollment entity and the Enrollment entity references the Course entity.

資料庫建立時,EF 會建立和 DbSet 屬性名稱相同的資料表。When the database is created, EF creates tables that have names the same as the DbSet property names. 集合的屬性名稱通常都是複數 (Students 而非 Student),但許多開發人員會為了資料表名稱究竟是否該是複數型態而爭論。Property names for collections are typically plural (Students rather than Student), but developers disagree about whether table names should be pluralized or not. 在此系列教學課程中,您會藉由指定 DbContext 中的單數資料表名稱來覆寫預設行為。For these tutorials you'll override the default behavior by specifying singular table names in the DbContext. 若要完成這項操作,請在最後一個 DbSet 屬性後方新增下列醒目提示程式碼。To do that, add the following highlighted code after the last DbSet property.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
        {
        }

        public DbSet<Course> Courses { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

建置專案以檢查編譯器錯誤。Build the project as a check for compiler errors.

註冊 SchoolContextRegister the SchoolContext

根據預設,ASP.NET Core 會實作相依性插入ASP.NET Core implements dependency injection by default. 服務 (例如 EF 資料庫內容) 是在應用程式啟動期間使用相依性插入來註冊。Services (such as the EF database context) are registered with dependency injection during application startup. 接著,會透過建構函式參數,針對需要這些服務的元件 (例如 MVC 控制器) 來提供服務。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.

若要將 SchoolContext 註冊為服務,請開啟 Startup.cs,並將醒目標示的程式碼新增至 ConfigureServices 方法。To register SchoolContext as a service, open Startup.cs, and add the highlighted lines to the ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddMvc();
}

連接字串的名稱,會透過呼叫 DbContextOptionsBuilder 物件上的方法來傳遞至內容。The name of the connection string is passed in to the context by calling a method on a DbContextOptionsBuilder object. 針對本機開發, ASP.NET Core 設定系統 會從檔案讀取連接字串 appsettings.jsonFor local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

ContosoUniversity.DataMicrosoft.EntityFrameworkCore 命名空間新增 using 陳述式,然後建置專案。Add using statements for ContosoUniversity.Data and Microsoft.EntityFrameworkCore namespaces, and then build the project.

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;

開啟檔案 appsettings.json ,並加入連接字串,如下列範例所示。Open the appsettings.json file and add a connection string as shown in the following example.

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

SQL Server Express LocalDBSQL Server Express LocalDB

連接字串會指定 SQL Server LocalDB 資料庫。The connection string specifies a SQL Server LocalDB database. LocalDB 是輕量版的 SQL Server Express Database Engine,旨在用於應用程式開發,而不是生產用途。LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for application development, not production use. LocalDB 會依需求啟動,並以使用者模式執行,因此沒有複雜的組態。LocalDB starts on demand and runs in user mode, so there's no complex configuration. LocalDB 預設會在 C:/Users/<user> 目錄中建立 .mdf 資料庫檔案。By default, LocalDB creates .mdf database files in the C:/Users/<user> directory.

使用測試資料將 DB 初始化Initialize DB with test data

Entity Framework 會為您建立空白資料庫。The Entity Framework will create an empty database for you. 在本節中,您會撰寫一個方法,該方法會在資料庫建立之後呼叫,以將測試資料填入資料庫。In this section, you write a method that's called after the database is created in order to populate it with test data.

在此您將使用 EnsureCreated 方法來自動建立資料庫。Here you'll use the EnsureCreated method to automatically create the database. 稍後的教學課程中,您將會了解到如何使用 Code First 移轉變更資料庫結構描述,而非卸除並重新建立資料庫,來處理模型的變更。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.

Data 資料夾中,建立一個名為 DbInitializer.cs 的新類別檔案,使用下列程式碼取代範本程式碼。這些程式碼會在需要的時候建立資料庫,並將測試資料載入至新的資料庫。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.

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

程式碼會檢查資料庫中是否有任何學生。若沒有的話,它便會假設資料庫是新的資料庫,因此需要植入測試資料。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. 會將測試資料載入至陣列而非 List<T> 集合,以最佳化效能。It loads test data into arrays rather than List<T> collections to optimize performance.

Program.cs 中,修改 Main 方法來在應用程式啟動期間執行下列動作:In Program.cs, modify the Main method to do the following on application startup:

  • 從相依性插入容器中取得資料庫內容執行個體。Get a database context instance from the dependency injection container.
  • 呼叫種子方法,並將其傳遞給內容。Call the seed method, passing to it the context.
  • 種子方法完成時處理內容。Dispose the context when the seed method is done.
using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

當您第一次執行應用程式時,將會建立資料庫並植入測試資料。The first time you run the application, the database will be created and seeded with test data. 每當您變更資料模型時:Whenever you change the data model:

  • 刪除資料庫。Delete the database.
  • 更新種子方法,並以相同方式開始使用新的資料庫再次。Update the 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 controller and views

在本節中,Visual Studio 中的樣板引擎會用來新增 MVC 控制器和將使用 EF 來查詢和儲存資料的視圖。In this section, the scaffolding engine in Visual Studio is used to add an MVC controller and views that will use EF to query and save data.

自動建立 CRUD 動作方法和檢視稱為 Scaffolding。The automatic creation of CRUD action methods and views is known as scaffolding. Scaffolding 與產生程式碼不同。Scaffold 程式碼是一個開始點,使得您可以修改它以符合您的需求,然而您通常不會去修改產生的程式碼。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.

  • 在方案總管中的 Controllers 資料夾上以滑鼠右鍵按一下,然後選取 [新增] > [新增 Scaffold 項目]。Right-click the Controllers folder in Solution Explorer and select Add > New Scaffolded Item.
  • 在 [新增 Scaffold] 對話方塊中:In the Add Scaffold dialog box:
    • 選取 [使用 Entity Framework 執行檢視的 MVC 控制器]。Select MVC controller with views, using Entity Framework.
    • 按一下 [新增]Click Add. [ Entity Framework 使用 Scaffold 新增 MVC 控制器 ] 對話方塊隨即出現:  StudentThe Add MVC Controller with views, using Entity Framework dialog box appears: Scaffold Student
    • 在 [模型類別] 中,選取 [Student]。In Model class select Student.
    • 在 [資料內容類別] 中,選取 [SchoolContext]。In Data context class select SchoolContext.
    • 接受預設的 StudentsController 作為名稱。Accept the default StudentsController as the name.
    • 按一下 [新增]Click Add.

Visual Studio 的樣板引擎會建立一個 >studentscontroller.cs .cs 檔案,以及一組與控制器搭配使用的 (. cshtml 檔案) 。The Visual Studio scaffolding engine creates a StudentsController.cs file and a set of views (.cshtml files) that work with the controller.

請注意,控制器會採用作為函式 SchoolContext 參數。Notice the controller takes a SchoolContext as a constructor parameter.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

ASP.NET Core 相依性插入會負責傳遞 SchoolContext 的執行個體給控制器。ASP.NET Core dependency injection takes care of passing an instance of SchoolContext into the controller. 啟動 .cs 檔案中設定。That was configured in the Startup.cs file.

控制器含有一個 Index 動作方法,該方法會顯示資料庫中的所有學生。The controller contains an Index action method, which displays all students in the database. 方法會藉由讀取資料庫內容執行個體的 Students 屬性,來從 Students 實體集中取得學生的清單:The method gets a list of students from the Students entity set by reading the Students property of the database context instance:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

您稍後會在本教學課程中瞭解此程式碼中的非同步程式設計項目。You learn about the asynchronous programming elements in this code later in the tutorial.

Views/Students/Index.cshtml 檢視會在一個資料表中顯示這份清單:The Views/Students/Index.cshtml view displays this list in a table:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

按 CTRL+F5 來執行專案,或從功能表選擇 [偵錯] > [啟動但不偵錯]。Press CTRL+F5 to run the project or choose Debug > Start Without Debugging from the menu.

按一下 [Students] 索引標籤來查看 DbInitializer.Initialize 方法插入的測試資料。Click the Students tab to see the test data that the DbInitializer.Initialize method inserted. 取決於您瀏覽器視窗的寬度,您可能會在頁面的頂端看到 Students 索引標籤連結,或是按一下位於右上角的導覽圖示來查看連結。Depending on how narrow your browser window is, you'll see the Students 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 大學首頁 (窄)

Students [索引] 頁面

檢視資料庫View the database

當您啟動應用程式時,DbInitializer.Initialize 方法會呼叫 EnsureCreatedWhen you started the application, the DbInitializer.Initialize method calls EnsureCreated. EF 看到不存在任何資料庫,於是便建立了一個資料庫,接著 Initialize 方法程式碼的剩餘部分便會將資料填入資料庫。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. 您可以使用 [SQL Server 物件總管 (SSOX) 來在 Visual Studio 中檢視資料庫。You can use SQL Server Object Explorer (SSOX) to view the database in Visual Studio.

關閉瀏覽器。Close the browser.

若 SSOX 視窗尚未開啟,請從 Visual Studio 中的 [檢視] 功能表選取它。If the SSOX window isn't already open, select it from the View menu in Visual Studio.

在 [SSOX] 中,按一下 (localdb) \mssqllocaldb > 資料庫],然後在檔案中的連接字串中,按一下資料庫名稱的專案 appsettings.jsonIn SSOX, click (localdb)\MSSQLLocalDB > Databases, and then click the entry for the database name that's in the connection string in the appsettings.json file.

展開 [ 資料表] 節點,查看資料庫中的資料表。Expand the Tables node to see the tables in the database.

SSOX 中的資料表

以滑鼠右鍵按一下 Students 資料表,並按一下 [檢視資料] 查看建立的資料行及插入資料表中的資料列。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.

SSOX 中的 Student 資料表

.mdf.ldf 資料庫檔案位於 C:\Users\<username> 資料夾中。The .mdf and .ldf database files are in the C:\Users\<username> folder.

因為您在應用程式啟動時執行的初始設定式方法中呼叫了 EnsureCreated,您現在可以對 Student 類別進行變更、刪除資料庫、重新執行應用程式,資料庫會自動重新建立以符合您所作出的變更。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. 例如,若您將一個 EmailAddress 屬性新增到 Student 類別,您便會在重新建立的資料表中看到新的 EmailAddress 資料行。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

為了讓 Entity Framework 能夠建立一個完整資料庫,您所需要撰寫的程式碼非常少,多虧了慣例的使用及 Entity Framework 所做出的假設。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.

  • DbSet 屬性的名稱會用於資料表名稱。The names of DbSet properties are used as table names. 針對 DbSet 屬性並未參考的實體,實體類別名稱會用於資料表名稱。For entities not referenced by a DbSet property, entity class names are used as table names.
  • 實體屬性名稱會用於資料行名稱。Entity property names are used for column names.
  • 命名為 ID 或 classnameID 的實體屬性,會辨識為主索引鍵屬性。Entity properties that are named ID or classnameID are recognized as primary key properties.
  • 如果屬性名為 (的外鍵屬性,則會將其視為外鍵屬性 <navigation property name><primary key property name> ,例如, StudentID Student 因為 Student 實體的主鍵是 ID) 。A property is interpreted 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). 外鍵屬性也可以簡單命名 <primary key property name> (例如, EnrollmentID 因為 Enrollment 實體的主鍵是 EnrollmentID) 。Foreign key properties can also be named simply <primary key property name> (for example, EnrollmentID since the Enrollment entity's primary key is EnrollmentID).

慣例行為可以被覆寫。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

非同步程式設計是預設的 ASP.NET Core 和 EF Core 模式。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. 使用同步程式碼,許多執行緒可能在實際上並未執行任何工作時受到占用,原因是在等候 I/O 完成。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. 使用非同步程式碼,處理程序在等候 I/O 完成時,其執行緒將會空出來以讓伺服器處理其他要求。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.

在下列程式碼中,async 關鍵字、Task<T> 傳回值、await 關鍵字和 ToListAsync 方法使程式碼以非同步方式執行。In the following code, the async keyword, Task<T> return value, await keyword, and ToListAsync method make the code execute asynchronously.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • async 關鍵字會告訴編譯器為方法本體的一部分產生回呼,並自動建立傳回的 Task<IActionResult> 物件。The async keyword tells the compiler to generate callbacks for parts of the method body and to automatically create the Task<IActionResult> object that's returned.
  • 傳回類型 Task<IActionResult> 代表了正在進行的工作,其結果為 IActionResult 類型。The return type Task<IActionResult> represents ongoing work with a result of type IActionResult.
  • await 關鍵字會使編譯器將方法分割為兩部分。The await keyword causes the compiler to split the method into two parts. 第一個部分會與非同步啟動的作業一同結束。The first part ends with the operation that's started asynchronously. 第二個部分則會放入作業完成時呼叫的回呼方法中。The second part is put into a callback method that's called when the operation completes.
  • ToListAsyncToList 擴充方法的非同步版本。ToListAsync is the asynchronous version of the ToList extension method.

當您在撰寫使用 Entity Framework 的非同步程式碼時,請注意下列事項:Some things to be aware of when you are writing asynchronous code that uses the Entity Framework:

  • 只有讓查詢或命令傳送至資料庫的陳述式,會以非同步方式執行。Only statements that cause queries or commands to be sent to the database are executed asynchronously. 其中包含,例如 ToListAsyncSingleOrDefaultAsync,以及 SaveChangesAsyncThat includes, for example, ToListAsync, SingleOrDefaultAsync, and SaveChangesAsync. 其中不包含,例如:僅變更 IQueryable 的陳述式,例如 var students = context.Students.Where(s => s.LastName == "Davolio")It doesn't include, for example, statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • EF 內容在執行緒中並不安全:不要嘗試執行多個平行作業。An EF context isn't thread safe: don't try to do multiple operations in parallel. 當您呼叫任何 async EF 方法時,請一律使用 await 關鍵字。When you call any async EF method, always use the await keyword.
  • 若您想要充分利用非同步程式碼帶來的效能優點,請確保任何您正在使用的程式庫 (例如分頁) 也使用了非同步 (若它們有呼叫任何可能會傳送查詢到資料庫的 Entity Framework 方法的話)。If you want to take advantage of the performance benefits of async code, make sure that any library packages that you're using (such as for paging), also use async if they call any Entity Framework methods that cause queries to be sent to the database.

如需在 .NET 中非同步程式設計的詳細資訊,請參閱非同步總覽For more information about asynchronous programming in .NET, see Async Overview.

下一步Next steps

若要了解如何執行基本的 CRUD (建立、讀取、更新、刪除) 作業,請前往下一個教學課程。Advance to the next tutorial to learn how to perform basic CRUD (create, read, update, delete) operations.