Razor ASP.NET Core 中有 Entity Framework Core 的頁面-教學課程 1/8Razor Pages with Entity Framework Core in ASP.NET Core - Tutorial 1 of 8

作者:Tom DykstraRick AndersonBy Tom Dykstra and Rick Anderson

這是一系列教學課程中的第一個教學課程,示範如何在 ASP.NET Core Razor Pages 應用程式中使用 Entity Framework (EF) Core。This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. 教學課程會為虛構的 Contoso 大學建置網站。The tutorials build a web site for a fictional Contoso University. 網站包含學生入學許可、課程建立和講師指派等功能。The site includes functionality such as student admission, course creation, and instructor assignments. 本教學課程使用 code first 方法。The tutorial uses the code first approach. 如需使用 database first 方法來遵循本教學課程的詳細資訊,請參閱 此 Github 問題For information on following this tutorial using the database first approach, see this Github issue.

下載或檢視已完成的應用程式。Download or view the completed app. 下載指示Download instructions.

PrerequisitesPrerequisites

  • 如果您還不熟悉 Razor 頁面,請先流覽「開始 使用 Razor 頁面 」教學課程系列,再開始此課程。If you're new to Razor Pages, go through the Get started with Razor Pages 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.

Visual Studio Code 說明則會使用 SQLite,它是一種跨平台的資料庫引擎。The Visual Studio Code instructions use SQLite, a cross-platform database engine.

若您選擇使用 SQLite,請下載及安裝協力廠商工具來管理和檢視 SQLite 資料庫,例如 DB Browser for SQLiteIf you choose to use SQLite, download and install a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.

疑難排解Troubleshooting

若您遇到無法解決的問題,請將您的程式碼與已完成的專案進行比較。If you run into a problem you can't resolve, compare your code to the completed project. 取得協助的其中一種好方法是將問題張貼到 StackOverflow.com,並使用 ASP.NET Core 標籤EF Core 標籤A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.

範例應用程式The sample 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 created in the tutorial.

Students [索引] 頁面

Students [編輯] 頁面

本網站的 UI 風格是以內建的專案範本為基礎。The UI style of this site is based on the built-in project templates. 本教學課程的重點在於如何搭配使用 EF Core 與 ASP.NET Core,而不是如何自訂 UI。The tutorial's focus is on how to use EF Core with ASP.NET Core, not how to customize the UI.

建立 Web 應用程式專案Create the web app project

  • 從 Visual Studio 的 [檔案]**** 功能表中,選取 [新增][專案] > **** 。From the Visual Studio File menu, select New > Project.
  • 選取 ASP.NET Core Web 應用程式Select ASP.NET Core Web Application.
  • 將專案命名為 ContosoUniversityName the project ContosoUniversity. 使用與此名稱完全相符的名稱非常重要 (包括大寫),這樣做可以讓命名空間在您複製和貼上程式碼時相符。It's important to use this exact name including capitalization, so the namespaces match when code is copied and pasted.
  • 在下拉式清單中選取 [ .Net Core ] 和 [ ASP.NET Core 5.0 ],然後選取 [ Web 應用程式]。Select .NET Core and ASP.NET Core 5.0 in the dropdowns, and then select Web Application.

設定網站樣式Set up the site style

將下列程式碼複製並貼入 Pages/Shared/_Layout cshtml 檔案: [!code-cshtmlMain]Copy and paste the following code into the Pages/Shared/_Layout.cshtml file: [!code-cshtmlMain]

版面配置檔案會設定網站頁首、頁尾和功能表。The layout file sets the site header, footer, and menu. 上述程式碼會進行下列變更:The preceding code makes the following changes:

  • 每次出現 "ContosoUniversity" 至 "Contoso 大學"。Each occurrence of "ContosoUniversity" to "Contoso University". 共有三個發生次數。There are three occurrences.
  • [ 首頁 ] 和 [ 隱私權 ] 功能表項目會被刪除。The Home and Privacy menu entries are deleted.
  • 針對「 關於」、「 學生」、「 課程」、「 講師」和「 部門」新增專案。Entries are added for About, Students, Courses, Instructors, and Departments.

Pages/Index. cshtml中,以下列程式碼取代檔案的內容:In Pages/Index.cshtml, replace the contents of the file with the following code:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
                </p>
            </div>
        </div>
    </div>
</div>

上述程式碼會將關於 ASP.NET Core 的文字取代為此應用程式的相關文字。The preceding code replaces the text about ASP.NET Core with text about this app.

執行應用程式來驗證首頁是否正常顯示。Run the app to verify that the home page appears.

資料模型The data model

下列各節會建立資料模型:The following sections create a data model:

Course-Enrollment-Student 資料模型圖表

學生可以註冊任何數量的課程,課程也能讓任意數量的學生註冊。A student can enroll in any number of courses, and a course can have any number of students enrolled in it.

Student 實體The Student entity

Student 實體圖表

  • 在專案資料夾中建立 Models 資料夾。Create a Models folder in the project folder.

  • 使用下列程式碼建立 Models/Student.csCreate Models/Student.cs 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 becomes the primary key column of the database table that corresponds to this class. EF Core 預設會解譯名為 IDclassnameID 作為主索引鍵的屬性。By default, EF Core interprets a property that's named ID or classnameID as the primary key. 因此 Student 類別主索引鍵的替代自動識別名稱為 StudentIDSo the alternative automatically recognized name for the Student class primary key is StudentID. 如需詳細資訊,請參閱 EF Core 索引鍵For more information, see EF Core - Keys.

Enrollments 屬性為導覽屬性The Enrollments property is a navigation property. 導覽屬性會保留與此實體相關的其他實體。Navigation properties hold other entities that are related to this entity. 在這種情況下,Student 實體的 Enrollments 屬性會保留所有與該 Student 相關的 Enrollment 實體。In this case, the Enrollments property of a Student entity holds all of the Enrollment entities that are related to that Student. 例如,若資料庫中的 Student 資料列有兩個相關的 Enrollment 資料列,則 Enrollments 導覽屬性便會包含這兩個 Enrollment 項目。For example, if a Student row in the database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.

在資料庫中,若 Enrollment 資料列的 StudentID 資料行包含學生的識別碼值,則該資料列便會與 Student 資料列相關。In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. 例如,假設某 Student 資料列的識別碼為 1。For example, suppose a Student row has ID=1. 相關的 Enrollment 資料列將會擁有 StudentID = 1。Related Enrollment rows will have StudentID = 1. StudentID 是 Enrollment 資料表中的「外部索引鍵」**。StudentID is a foreign key in the Enrollment table.

Enrollments 屬性會定義為 ICollection<Enrollment>,因為可能會有多個相關的 Enrollment 實體。The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. 您可以使用其他集合型別,例如 List<Enrollment>HashSet<Enrollment>You can use other collection types, such as List<Enrollment> or HashSet<Enrollment>. 如使用 ICollection<Enrollment>,EF Core 預設將建立 HashSet<Enrollment> 集合。When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.

Enrollment 實體The Enrollment entity

Enrollment 實體圖表

使用下列程式碼建立 Models/Enrollment.csCreate Models/Enrollment.cs 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 模式,而非 ID 本身。The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. 針對生產資料模型,請選擇一個模式並一致地使用它。For a production data model, choose one pattern and use it consistently. 本教學課程同時使用兩者的方式只是為了示範兩者都可運作。This tutorial uses both just to illustrate that both work. 在不使用 classname 的情況下使用 ID 可讓實作某些類型的資料模型變更更容易。Using ID without classname makes it easier to implement some kinds of data model changes.

Grade 屬性為一個 enumThe Grade property is an enum. Grade 型別宣告後方的問號表示 Grade 屬性可為 NullThe 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 實體。An Enrollment entity is associated with one Student entity, so the property contains a single Student entity.

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.

如果實體名為 <navigation property name><primary key property name>,則 EF Core 會將之解釋為外部索引鍵。EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>. 例如,StudentIDStudent 導覽屬性的外部索引鍵,因為 Student 實體的主索引鍵是 IDFor example,StudentID is the foreign key for the Student navigation property, since the Student entity's primary key is ID. 外部索引鍵屬性也可命名為 <primary key property name>Foreign key properties can also be named <primary key property name>. 例如 CourseID,因為 Course 實體的主索引鍵是 CourseIDFor example, CourseID since the Course entity's primary key is CourseID.

Course 實體The Course entity

Course 實體圖表

使用下列程式碼建立 Models/Course.csCreate Models/Course.cs 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 allows the app to specify the primary key rather than having the database generate it.

建置專案以驗證沒有任何編譯器錯誤。Build the project to validate that there are no compiler errors.

Scaffold Student 頁面Scaffold Student pages

在本節中,您會使用 ASP.NET scaffolding 工具來產生:In this section, you use the ASP.NET Core scaffolding tool to generate:

  • EF Core DbContext 類別。An EF Core DbContext class. 內容是協調指定資料模型 Entity Framework 功能的主類別。The context is the main class that coordinates Entity Framework functionality for a given data model. 它衍生自 Microsoft.EntityFrameworkCore.DbContext 類別。It derives from the Microsoft.EntityFrameworkCore.DbContext class.
  • Razor 處理實體之建立、讀取、更新和刪除 (CRUD) 作業的頁面 StudentRazor pages that handle Create, Read, Update, and Delete (CRUD) operations for the Student entity.
  • 建立 Pages/Students 資料夾。Create a Pages/Students folder.
  • 在 [方案總管]**** 中,以滑鼠右鍵按一下 Page/Students 資料夾,然後選取 [新增]** [新增 Scaffold 項目]** > ****。In Solution Explorer, right-click the Pages/Students folder and select Add > New Scaffolded Item.
  • 在 [ 加入新的 Scaffold 專案 ] 對話方塊中:In the Add New Scaffold Item dialog:
    • 在左側索引標籤中,選取 [已安裝 > 一般 > Razor 頁面]In the left tab, select Installed > Common > Razor Pages
    • ** Razor 使用 Entity Framework (CRUD) **新增] 選取頁面 > ** **。Select Razor Pages using Entity Framework (CRUD) > ADD.
  • 在 [ ** Razor 使用 Entity Framework 加入頁面] (CRUD) ** 對話方塊:In the Add Razor Pages using Entity Framework (CRUD) dialog:
    • 在 [模型類別]**** 下拉式清單中,選取 [學生 (ContosoUniversity.Models)]****。In the Model class drop-down, select Student (ContosoUniversity.Models).
    • 在 [資料內容類別]**** 資料列中,選取 + (加號)。In the Data context class row, select the + (plus) sign.
      • 將資料內容名稱變更為 end, SchoolContext 而不是 ContosoUniversityContextChange the data context name to end in SchoolContext rather than ContosoUniversityContext. 更新的內容名稱: ContosoUniversity.Data.SchoolContextThe updated context name: ContosoUniversity.Data.SchoolContext
    • 選取 [新增]。Select Add.

會自動安裝下列套件:The following packages are automatically installed:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

如果上述步驟失敗,請建立專案,然後重試 scaffold 步驟。If the preceding step fails, build the project and retry the scaffold step.

Scaffolding 流程:The scaffolding process:

  • Razor在 [頁面/學生] 資料夾中建立頁面:Creates Razor pages in the Pages/Students folder:
    • Create.cshtmlCreate.cshtml.csCreate.cshtml and Create.cshtml.cs
    • Delete.cshtmlDelete.cshtml.csDelete.cshtml and Delete.cshtml.cs
    • Details.cshtmlDetails.cshtml.csDetails.cshtml and Details.cshtml.cs
    • Edit.cshtmlEdit.cshtml.csEdit.cshtml and Edit.cshtml.cs
    • Index.cshtmlIndex.cshtml.csIndex.cshtml and Index.cshtml.cs
  • 建立 Data/SchoolContext.csCreates Data/SchoolContext.cs.
  • 將內容新增到 Startup.cs 中的相依性插入。Adds the context to dependency injection in Startup.cs.
  • 將資料庫連接字串新增到 appsettings.jsonAdds a database connection string to appsettings.json.

資料庫連接字串Database connection string

「樣板」工具會在 appsettings.json 檔案產生連接字串。The scaffolding tool generates a connection string in the appsettings.json file.

連接字串會指定 SQL Server LocalDBThe connection string specifies SQL Server LocalDB:

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

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 會在 C:/Users/<user> 目錄中建立 .mdf 檔案。By default, LocalDB creates .mdf files in the C:/Users/<user> directory.

更新資料庫內容類別Update the database context class

協調指定資料模型 EF Core 功能的主類別是資料庫內容類別。The main class that coordinates EF Core functionality for a given data model is the database context class. 內容衍生自 Microsoft.EntityFrameworkCore.DbContextThe context is derived from Microsoft.EntityFrameworkCore.DbContext. 內容會指定哪些實體會包含在資料模型中。The context specifies which entities are included in the data model. 在此專案中,類別命名為 SchoolContextIn this project, the class is named SchoolContext.

使用下列程式碼來更新 Data/SchoolContext.csUpdate Data/SchoolContext.cs with the following code:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

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

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

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

上述程式碼會從單數變更 DbSet<Student> Student 為複數 DbSet<Student> StudentsThe preceding code changes from the singular DbSet<Student> Student to the plural DbSet<Student> Students. 若要讓 Razor 頁面程式碼符合新的 DBSet 名稱,請從下列內容進行全域變更: _context.Student.To make the Razor Pages code match the new DBSet name, make a global change from: _context.Student. 自: _context.Students.to: _context.Students.

會有 8 次變更。There are 8 occurrences.

因為實體集包含多個實體,所以許多開發人員偏好 DBSet 屬性名稱應為複數。Because an entity set contains multiple entities, many developers prefer the DBSet property names should be plural.

反白顯示的程式碼:The highlighted code:

  • 建立每個實體集的DbSet <TEntity> 屬性。Creates a DbSet<TEntity> property for each entity set. 在 EF Core 用語中:In EF Core terminology:
    • 實體集通常會對應到資料庫資料表。An entity set typically corresponds to a database table.
    • 實體會對應至資料表中的資料列。An entity corresponds to a row in the table.
  • 呼叫 OnModelCreatingCalls OnModelCreating. OnModelCreating:OnModelCreating:
    • 當已 SchoolContext 初始化,但模型已鎖定並用來初始化內容之前,會呼叫。Is called when SchoolContext has been initialized, but before the model has been locked down and used to initialize the context.
    • 是必要的,因為稍後在教學課程中, Student 實體會有其他實體的參考。Is required because later in the tutorial The Student entity will have references to the other entities.

建置專案以確認沒有任何編譯器錯誤。Build the project to verify there are no compiler errors.

Startup.csStartup.cs

ASP.NET Core 內建相依性插入ASP.NET Core is built with dependency injection. 等服務 SchoolContext 會在應用程式啟動期間註冊相依性插入。Services such as the SchoolContext are registered with dependency injection during app startup. 需要這些服務的元件(例如 Razor 頁面)是透過函式參數提供這些服務。Components that require these services, such as Razor Pages, are provided these services via constructor parameters. 取得資料庫內容執行個體的建構函式程式碼會顯示在本教學課程稍後部分。The constructor code that gets a database context instance is shown later in the tutorial.

Scaffolding 工具會自動對相依性插入容器註冊內容類別。The scaffolding tool automatically registered the context class with the dependency injection container.

Scaffolder 已新增下列醒目提示的行:The following highlighted lines were added by the scaffolder:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

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

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

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

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

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

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

    services.AddDatabaseDeveloperPageExceptionFilter();
}

新增 AspNetCore Microsoft.entityframeworkcore NuGet 套件。Add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package.

在 PMC 中,輸入下列命令以新增 NuGet 套件:In the PMC, enter the following command to add the NuGet package:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore -Version 5.0.0-rc.1.20451.17

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

建立資料庫Create the database

若未存在資料庫,則請更新 Program.cs 來加以建立:Update Program.cs to create the database if it doesn't exist:

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>();
                    context.Database.EnsureCreated();
                    // 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>();
                });
    }
}

若已存在內容的資料庫,則 EnsureCreated 方法便不會採取任何動作。The EnsureCreated method takes no action if a database for the context exists. 若資料庫不存在,則它會建立資料庫和結構描述。If no database exists, it creates the database and schema. EnsureCreated 會啟用下列工作流程來處理資料模型變更:EnsureCreated enables the following workflow for handling data model changes:

  • 刪除資料庫。Delete the database. 任何現有的資料都會遺失。Any existing data is lost.
  • 變更資料模型。Change the data model. 例如,新增 EmailAddress 欄位。For example, add an EmailAddress field.
  • 執行應用程式。Run the app.
  • EnsureCreated 會使用新的結構描述來建立資料庫。EnsureCreated creates a database with the new schema.

只要您不需要保存資料,此工作流程在開發初期結構描述快速變化時的運作效果便會相當良好。This workflow works well early in development when the schema is rapidly evolving, as long as you don't need to preserve data. 當資料輸入資料庫且需要進行保存時,狀況則會不同。The situation is different when data that has been entered into the database needs to be preserved. 在這種情況下,請使用移轉。When that is the case, use migrations.

在教學課程系列的稍後部分,您會刪除 EnsureCreated 建立的資料庫並改為使用移轉。Later in the tutorial series, you delete the database that was created by EnsureCreated and use migrations instead. EnsureCreated 建立的資料庫無法使用移轉來更新。A database that is created by EnsureCreated can't be updated by using migrations.

測試應用程式Test the app

  • 執行應用程式。Run the app.
  • 選取 [學生]**** 連結,然後選取 [新建]****。Select the Students link and then Create New.
  • 測試 [編輯]、[詳細資料] 和 [刪除] 連結。Test the Edit, Details, and Delete links.

植入資料庫Seed the database

EnsureCreated 方法會建立空白資料庫。The EnsureCreated method creates an empty database. 本節會新增程式碼以使用測試資料來填入資料庫。This section adds code that populates the database with test data.

使用下列程式碼建立 Data/DbInitializer.csCreate Data/DbInitializer.cs with the following code:

using ContosoUniversity.Data;
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("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            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}
            };

            context.Courses.AddRange(courses);
            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},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

程式碼會檢查資料庫中是否有任何學生。The code checks if there are any students in the database. 若沒有任何學生,它便會將測試資料新增到資料庫。If there are no students, it adds test data to the database. 它會以陣列的方式建立測試資料,而非 List<T> 集合,來最佳化效能。It creates the test data in arrays rather than List<T> collections to optimize performance.

Program.cs 中,將 EnsureCreated 呼叫替換成 DbInitializer.Initialize 呼叫:In Program.cs, replace the EnsureCreated call with a DbInitializer.Initialize call:

// context.Database.EnsureCreated();
DbInitializer.Initialize(context);

停止應用程式 (如果它正在執行),並在套件管理員主控台 (PMC) 中執行下列命令:Stop the app if it's running, and run the following command in the Package Manager Console (PMC):

Drop-Database -Confirm

回應 Y 以刪除資料庫。Respond with Y to delete the database.

  • 重新啟動應用程式。Restart the app.
  • 選取 Students 頁面來查看植入的資料。Select the Students page to see the seeded data.

檢視資料庫View the database

  • 從 Visual Studio 中的 View 功能表開啟 SQL Server 物件總管 (SSOX)。Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio.
  • 在 SSOX 中,選取 [(localdb)\MSSQLLocalDB] > [資料庫] > [SchoolContext-{GUID}]****。In SSOX, select (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. 資料庫名稱是以您稍早所提供的內容名稱加上虛線和 GUID 所產生。The database name is generated from the context name you provided earlier plus a dash and a GUID.
  • 展開 Tables 節點。Expand the Tables node.
  • 以滑鼠右鍵按一下 Students 資料表,並按一下 [檢視資料]**** 查看建立的資料行、插入資料表中的資料列。Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.
  • 以滑鼠右鍵按一下 Student 資料表然後按一下 [檢視程式碼]**** 來查看 Student 模型對應到 Student 資料表結構描述的方式。Right-click the Student table and click View Code to see how the Student model maps to the Student table schema.

非同步程式碼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 doing 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 can handle more traffic without delays.

非同步程式碼會在執行階段導致少量的額外負荷。Asynchronous code does introduce a small amount of overhead at run time. 在低流量情況下,對效能的衝擊非常微小;在高流量情況下,潛在的效能改善則相當大。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 OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • async 關鍵字會指示編譯器:The async keyword tells the compiler to:
    • 為方法主體的組件產生回呼。Generate callbacks for parts of the method body.
    • 建立傳回的 Task 物件。Create the Task object that's returned.
  • Task<T> 傳回型別代表正在進行的工作。The Task<T> return type represents ongoing work.
  • 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 Core 的非同步程式碼,請注意下列事項:Some things to be aware of when writing asynchronous code that uses EF Core:

  • 只有讓查詢或命令傳送至資料庫的陳述式,會以非同步方式執行。Only statements that cause queries or commands to be sent to the database are executed asynchronously. 其中包含 ToListAsyncSingleOrDefaultAsyncFirstOrDefaultAsyncSaveChangesAsyncThat includes ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. 不會包含只變更 IQueryable 的陳述式,例如 var students = context.Students.Where(s => s.LastName == "Davolio")It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • EF Core 內容在執行緒中並不安全:不要嘗試執行多個平行作業。An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
  • 若要利用非同步程式碼所帶來的效能利益,請驗證該程式庫套件 (例如用於分頁) 在呼叫傳送查詢到資料庫的 EF Core 方法時使用 async。To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the database.

如需非同步方法的詳細資訊,請參閱 Async 概觀使用 Async 和 Await 設計非同步程式For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.

效能考量Performance considerations

一般而言,網頁不應該載入任意數目的資料列。In general, a web page shouldn't be loading an arbitrary number of rows. 查詢應該使用分頁或限制方法。A query should use paging or a limiting approach. 例如,上述查詢可以用 Take 來限制傳回的資料列:For example, the preceding query could use Take to limit the rows returned:

public class IndexModel : PageModel
{
    private readonly SchoolContext _context;
    private readonly MvcOptions _mvcOptions;

    public IndexModel(SchoolContext context, IOptions<MvcOptions> mvcOptions)
    {
        _context = context;
        _mvcOptions = mvcOptions.Value;
    }

    public IList<Student> Student { get;set; }

    public async Task OnGetAsync()
    {
        Student = await _context.Students.Take(
            _mvcOptions.MaxModelBindingCollectionSize).ToListAsync();
    }
}

如果資料庫例外狀況是透過列舉的一部分發生,則在 view 中列舉大型資料表可能會傳回部分建立的 HTTP 200 回應。Enumerating a large table in a view could return a partially constructed HTTP 200 response if a database exception occurs part way through the enumeration.

MaxModelBindingCollectionSize 預設為1024。MaxModelBindingCollectionSize defaults to 1024. 下列程式碼會設定 MaxModelBindingCollectionSizeThe following code sets MaxModelBindingCollectionSize:

public void ConfigureServices(IServiceCollection services)
{
    int MyMaxModelBindingCollectionSize = 100;
    Int32.TryParse(Configuration["MyMaxModelBindingCollectionSize"],
                               out MyMaxModelBindingCollectionSize);
    
    services.Configure<MvcOptions>(options => 
           options.MaxModelBindingCollectionSize = MyMaxModelBindingCollectionSize);

    services.AddRazorPages();

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

    services.AddDatabaseDeveloperPageExceptionFilter();
}

本教學課程稍後會討論分頁。Paging is covered later in the tutorial.

後續步驟Next steps

這是一系列教學課程中的第一個教學課程,示範如何在 ASP.NET Core Razor Pages 應用程式中使用 Entity Framework (EF) Core。This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. 教學課程會為虛構的 Contoso 大學建置網站。The tutorials build a web site for a fictional Contoso University. 網站包含學生入學許可、課程建立和講師指派等功能。The site includes functionality such as student admission, course creation, and instructor assignments. 本教學課程使用 code first 方法。The tutorial uses the code first approach. 如需使用 database first 方法來遵循本教學課程的詳細資訊,請參閱 此 Github 問題For information on following this tutorial using the database first approach, see this Github issue.

下載或檢視已完成的應用程式。Download or view the completed app. 下載指示Download instructions.

PrerequisitesPrerequisites

  • 如果您還不熟悉 Razor 頁面,請先流覽「開始 使用 Razor 頁面 」教學課程系列,再開始此課程。If you're new to Razor Pages, go through the Get started with Razor Pages 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.

Visual Studio Code 說明則會使用 SQLite,它是一種跨平台的資料庫引擎。The Visual Studio Code instructions use SQLite, a cross-platform database engine.

若您選擇使用 SQLite,請下載及安裝協力廠商工具來管理和檢視 SQLite 資料庫,例如 DB Browser for SQLiteIf you choose to use SQLite, download and install a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.

疑難排解Troubleshooting

若您遇到無法解決的問題,請將您的程式碼與已完成的專案進行比較。If you run into a problem you can't resolve, compare your code to the completed project. 取得協助的其中一種好方法是將問題張貼到 StackOverflow.com,並使用 ASP.NET Core 標籤EF Core 標籤A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.

範例應用程式The sample 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 created in the tutorial.

Students [索引] 頁面

Students [編輯] 頁面

本網站的 UI 風格是以內建的專案範本為基礎。The UI style of this site is based on the built-in project templates. 教學課程的重點在於如何使用 EF Core,而非如何自訂 UI。The tutorial's focus is on how to use EF Core, not how to customize the UI.

請遵循頁面頂端的連結來取得已完成專案的原始程式碼。Follow the link at the top of the page to get the source code for the completed project. cu30 資料夾包含本教學課程 ASP.NET Core 3.0 版本的程式碼。The cu30 folder has the code for the ASP.NET Core 3.0 version of the tutorial. 您可以在 cu30snapshots 資料夾中找到反映教學課程 1 到 7 程式碼狀態的檔案。Files that reflect the state of the code for tutorials 1-7 can be found in the cu30snapshots folder.

在下載已完成的專案後執行應用程式:To run the app after downloading the completed project:

  • 建置專案。Build the project.

  • 在套件管理器主控台 (PMC) 中,執行下列命令:In Package Manager Console (PMC) run the following command:

    Update-Database
    
  • 執行專案來植入資料庫。Run the project to seed the database.

建立 Web 應用程式專案Create the web app project

  • 從 Visual Studio 的 [檔案]**** 功能表中,選取 [新增][專案] > **** 。From the Visual Studio File menu, select New > Project.
  • 選取 ASP.NET Core Web 應用程式Select ASP.NET Core Web Application.
  • 將專案命名為 ContosoUniversityName the project ContosoUniversity. 使用與此名稱完全相符的名稱非常重要 (包括大寫),這樣做可以讓命名空間在您複製和貼上程式碼時相符。It's important to use this exact name including capitalization, so the namespaces match when code is copied and pasted.
  • 在下拉式清單中選取 [.NET Core]**** 及 [ASP.NET Core 3.0]****,然後選取 [Web 應用程式]****。Select .NET Core and ASP.NET Core 3.0 in the dropdowns, and then select Web Application.

設定網站樣式Set up the site style

更新 Pages/Shared/_Layout.cshtml 來設定網站頁首、頁尾和功能表:Set up the site header, footer, and menu by updating Pages/Shared/_Layout.cshtml:

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

  • 刪除 [Home]**** 和 [Privacy]**** 功能表項目,然後新增 [About]****、[Students]****、[Courses]****、[Instructors]**** 和 [Departments]**** 的項目。Delete the Home and Privacy menu entries, and add entries for About, Students, Courses, Instructors, and Departments.

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

<!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.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-page="/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-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/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; 2019 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

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

Pages/Index.cshtml 中,將檔案內容替換成下列程式碼,將 ASP.NET Core 相關文字取代成此應用程式的相關文字:In Pages/Index.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET Core with text about this app:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
                </p>
            </div>
        </div>
    </div>
</div>

執行應用程式來驗證首頁是否正常顯示。Run the app to verify that the home page appears.

資料模型The data model

下列各節會建立資料模型:The following sections create a data model:

Course-Enrollment-Student 資料模型圖表

學生可以註冊任何數量的課程,課程也能讓任意數量的學生註冊。A student can enroll in any number of courses, and a course can have any number of students enrolled in it.

Student 實體The Student entity

Student 實體圖表

  • 在專案資料夾中建立 Models 資料夾。Create a Models folder in the project folder.

  • 使用下列程式碼建立 Models/Student.csCreate Models/Student.cs 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 becomes the primary key column of the database table that corresponds to this class. EF Core 預設會解譯名為 IDclassnameID 作為主索引鍵的屬性。By default, EF Core interprets a property that's named ID or classnameID as the primary key. 因此 Student 類別主索引鍵的替代自動識別名稱為 StudentIDSo the alternative automatically recognized name for the Student class primary key is StudentID. 如需詳細資訊,請參閱 EF Core 索引鍵For more information, see EF Core - Keys.

Enrollments 屬性為導覽屬性The Enrollments property is a navigation property. 導覽屬性會保留與此實體相關的其他實體。Navigation properties hold other entities that are related to this entity. 在這種情況下,Student 實體的 Enrollments 屬性會保留所有與該 Student 相關的 Enrollment 實體。In this case, the Enrollments property of a Student entity holds all of the Enrollment entities that are related to that Student. 例如,若資料庫中的 Student 資料列有兩個相關的 Enrollment 資料列,則 Enrollments 導覽屬性便會包含這兩個 Enrollment 項目。For example, if a Student row in the database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.

在資料庫中,若 Enrollment 資料列的 StudentID 資料行包含學生的識別碼值,則該資料列便會與 Student 資料列相關。In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. 例如,假設某 Student 資料列的識別碼為 1。For example, suppose a Student row has ID=1. 相關的 Enrollment 資料列將會擁有 StudentID = 1。Related Enrollment rows will have StudentID = 1. StudentID 是 Enrollment 資料表中的「外部索引鍵」**。StudentID is a foreign key in the Enrollment table.

Enrollments 屬性會定義為 ICollection<Enrollment>,因為可能會有多個相關的 Enrollment 實體。The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. 您可以使用其他集合型別,例如 List<Enrollment>HashSet<Enrollment>You can use other collection types, such as List<Enrollment> or HashSet<Enrollment>. 如使用 ICollection<Enrollment>,EF Core 預設將建立 HashSet<Enrollment> 集合。When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.

Enrollment 實體The Enrollment entity

Enrollment 實體圖表

使用下列程式碼建立 Models/Enrollment.csCreate Models/Enrollment.cs 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 模式,而非 ID 本身。The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. 針對生產資料模型,請選擇一個模式並一致地使用它。For a production data model, choose one pattern and use it consistently. 本教學課程同時使用兩者的方式只是為了示範兩者都可運作。This tutorial uses both just to illustrate that both work. 在不使用 classname 的情況下使用 ID 可讓實作某些類型的資料模型變更更容易。Using ID without classname makes it easier to implement some kinds of data model changes.

Grade 屬性為一個 enumThe Grade property is an enum. Grade 型別宣告後方的問號表示 Grade 屬性可為 NullThe 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 實體。An Enrollment entity is associated with one Student entity, so the property contains a single Student entity.

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.

如果實體名為 <navigation property name><primary key property name>,則 EF Core 會將之解釋為外部索引鍵。EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>. 例如,StudentIDStudent 導覽屬性的外部索引鍵,因為 Student 實體的主索引鍵是 IDFor example,StudentID is the foreign key for the Student navigation property, since the Student entity's primary key is ID. 外部索引鍵屬性也可命名為 <primary key property name>Foreign key properties can also be named <primary key property name>. 例如 CourseID,因為 Course 實體的主索引鍵是 CourseIDFor example, CourseID since the Course entity's primary key is CourseID.

Course 實體The Course entity

Course 實體圖表

使用下列程式碼建立 Models/Course.csCreate Models/Course.cs 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 allows the app to specify the primary key rather than having the database generate it.

建置專案以驗證沒有任何編譯器錯誤。Build the project to validate that there are no compiler errors.

Scaffold Student 頁面Scaffold Student pages

在本節中,您會使用 ASP.NET scaffolding 工具來產生:In this section, you use the ASP.NET Core scaffolding tool to generate:

  • EF Core「內容」** 類別。An EF Core context class. 內容是協調指定資料模型 Entity Framework 功能的主類別。The context is the main class that coordinates Entity Framework functionality for a given data model. 它衍生自 Microsoft.EntityFrameworkCore.DbContext 類別。It derives from the Microsoft.EntityFrameworkCore.DbContext class.
  • Razor 處理實體之建立、讀取、更新和刪除 (CRUD) 作業的頁面 StudentRazor pages that handle Create, Read, Update, and Delete (CRUD) operations for the Student entity.
  • Pages 資料夾中建立 Students 資料夾。Create a Students folder in the Pages folder.
  • 在 [方案總管]**** 中,以滑鼠右鍵按一下 Page/Students 資料夾,然後選取 [新增]** [新增 Scaffold 項目]** > ****。In Solution Explorer, right-click the Pages/Students folder and select Add > New Scaffolded Item.
  • 在 [新增 Scaffold ] 對話方塊中, ** Razor 使用 Entity Framework (CRUD) **新增] 選取 [頁面] > ** **。In the Add Scaffold dialog, select Razor Pages using Entity Framework (CRUD) > ADD.
  • 在 [ ** Razor 使用 Entity Framework 加入頁面] (CRUD) ** 對話方塊:In the Add Razor Pages using Entity Framework (CRUD) dialog:
    • 在 [模型類別]**** 下拉式清單中,選取 [學生 (ContosoUniversity.Models)]****。In the Model class drop-down, select Student (ContosoUniversity.Models).
    • 在 [資料內容類別]**** 資料列中,選取 + (加號)。In the Data context class row, select the + (plus) sign.
    • 將資料內容的名稱從 ContosoUniversity.Models.ContosoUniversityContext 變更為 ContosoUniversity.Data.SchoolContextChange the data context name from ContosoUniversity.Models.ContosoUniversityContext to ContosoUniversity.Data.SchoolContext.
    • 選取 [新增]。Select Add.

會自動安裝下列套件:The following packages are automatically installed:

  • Microsoft.VisualStudio.Web.CodeGeneration.Design
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.Extensions.Logging.Debug
  • Microsoft.EntityFrameworkCore.Tools

若您在上述步驟中遇到問題,請建置專案並重試 scaffold 步驟。If you have a problem with the preceding step, build the project and retry the scaffold step.

Scaffolding 流程:The scaffolding process:

  • Razor在 [頁面/學生] 資料夾中建立頁面:Creates Razor pages in the Pages/Students folder:
    • Create.cshtmlCreate.cshtml.csCreate.cshtml and Create.cshtml.cs
    • Delete.cshtmlDelete.cshtml.csDelete.cshtml and Delete.cshtml.cs
    • Details.cshtmlDetails.cshtml.csDetails.cshtml and Details.cshtml.cs
    • Edit.cshtmlEdit.cshtml.csEdit.cshtml and Edit.cshtml.cs
    • Index.cshtmlIndex.cshtml.csIndex.cshtml and Index.cshtml.cs
  • 建立 Data/SchoolContext.csCreates Data/SchoolContext.cs.
  • 將內容新增到 Startup.cs 中的相依性插入。Adds the context to dependency injection in Startup.cs.
  • 將資料庫連接字串新增到 appsettings.jsonAdds a database connection string to appsettings.json.

資料庫連接字串Database connection string

檔案 appsettings.js 會指定 SQL Server LocalDB的連接字串。The appsettings.json file specifies the connection string SQL Server LocalDB.

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

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 會在 C:/Users/<user> 目錄中建立 .mdf 檔案。By default, LocalDB creates .mdf files in the C:/Users/<user> directory.

更新資料庫內容類別Update the database context class

協調指定資料模型 EF Core 功能的主類別是資料庫內容類別。The main class that coordinates EF Core functionality for a given data model is the database context class. 內容衍生自 Microsoft.EntityFrameworkCore.DbContextThe context is derived from Microsoft.EntityFrameworkCore.DbContext. 內容會指定哪些實體會包含在資料模型中。The context specifies which entities are included in the data model. 在此專案中,類別命名為 SchoolContextIn this project, the class is named SchoolContext.

使用下列程式碼來更新 Data/SchoolContext.csUpdate Data/SchoolContext.cs with the following code:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

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

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

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

反白顯示的程式碼會為每個實體集建立DbSet <TEntity> 屬性。The highlighted code creates a DbSet<TEntity> property for each entity set. 在 EF Core 用語中:In EF Core terminology:

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

因為實體集會包含多個實體,所以 DBSet 屬性應為複數名稱。Since an entity set contains multiple entities, the DBSet properties should be plural names. 因為 scaffolding 工具建立了 Student DBSet,所以此步驟會將它變更為複數的 StudentsSince the scaffolding tool created aStudent DBSet, this step changes it to plural Students.

若要讓 Razor 頁面程式碼符合新的 DBSet 名稱,請在的整個專案之間進行全域 _context.Student 變更 _context.StudentsTo make the Razor Pages code match the new DBSet name, make a global change across the whole project of _context.Student to _context.Students. 會有 8 次變更。There are 8 occurrences.

建置專案以確認沒有任何編譯器錯誤。Build the project to verify there are no compiler errors.

Startup.csStartup.cs

ASP.NET Core 內建相依性插入ASP.NET Core is built with dependency injection. 服務 (例如 EF Core 資料庫內容) 會在應用程式啟動期間對相依性插入進行註冊。Services (such as the EF Core database context) are registered with dependency injection during application startup. 需要這些服務的元件 (例如 Razor 頁面) 是透過函式參數提供這些服務。Components that require these services (such as Razor Pages) are provided these services via constructor parameters. 取得資料庫內容執行個體的建構函式程式碼會顯示在本教學課程稍後部分。The constructor code that gets a database context instance is shown later in the tutorial.

Scaffolding 工具會自動對相依性插入容器註冊內容類別。The scaffolding tool automatically registered the context class with the dependency injection container.

  • ConfigureServices 中,Scaffolder 會新增下列醒目提示行:In ConfigureServices, the highlighted lines were added by the scaffolder:

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

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

建立資料庫Create the database

若未存在資料庫,則請更新 Program.cs 來加以建立:Update Program.cs to create the database if it doesn't exist:

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>();
                    context.Database.EnsureCreated();
                    // 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>();
                });
    }
}

若已存在內容的資料庫,則 EnsureCreated 方法便不會採取任何動作。The EnsureCreated method takes no action if a database for the context exists. 若資料庫不存在,則它會建立資料庫和結構描述。If no database exists, it creates the database and schema. EnsureCreated 會啟用下列工作流程來處理資料模型變更:EnsureCreated enables the following workflow for handling data model changes:

  • 刪除資料庫。Delete the database. 任何現有的資料都會遺失。Any existing data is lost.
  • 變更資料模型。Change the data model. 例如,新增 EmailAddress 欄位。For example, add an EmailAddress field.
  • 執行應用程式。Run the app.
  • EnsureCreated 會使用新的結構描述來建立資料庫。EnsureCreated creates a database with the new schema.

只要您不需要保存資料,此工作流程在開發初期結構描述快速變化時的運作效果便會相當良好。This workflow works well early in development when the schema is rapidly evolving, as long as you don't need to preserve data. 當資料輸入資料庫且需要進行保存時,狀況則會不同。The situation is different when data that has been entered into the database needs to be preserved. 在這種情況下,請使用移轉。When that is the case, use migrations.

在教學課程系列的稍後部分,您會刪除 EnsureCreated 建立的資料庫並改為使用移轉。Later in the tutorial series, you delete the database that was created by EnsureCreated and use migrations instead. EnsureCreated 建立的資料庫無法使用移轉來更新。A database that is created by EnsureCreated can't be updated by using migrations.

測試應用程式Test the app

  • 執行應用程式。Run the app.
  • 選取 [學生]**** 連結,然後選取 [新建]****。Select the Students link and then Create New.
  • 測試 [編輯]、[詳細資料] 和 [刪除] 連結。Test the Edit, Details, and Delete links.

植入資料庫Seed the database

EnsureCreated 方法會建立空白資料庫。The EnsureCreated method creates an empty database. 本節會新增程式碼以使用測試資料來填入資料庫。This section adds code that populates the database with test data.

使用下列程式碼建立 Data/DbInitializer.csCreate Data/DbInitializer.cs with the following code:

using ContosoUniversity.Data;
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("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            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}
            };

            context.Courses.AddRange(courses);
            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},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

程式碼會檢查資料庫中是否有任何學生。The code checks if there are any students in the database. 若沒有任何學生,它便會將測試資料新增到資料庫。If there are no students, it adds test data to the database. 它會以陣列的方式建立測試資料,而非 List<T> 集合,來最佳化效能。It creates the test data in arrays rather than List<T> collections to optimize performance.

  • Program.cs 中,將 EnsureCreated 呼叫替換成 DbInitializer.Initialize 呼叫:In Program.cs, replace the EnsureCreated call with a DbInitializer.Initialize call:

    // context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
    

停止應用程式 (如果它正在執行),並在套件管理員主控台 (PMC) 中執行下列命令:Stop the app if it's running, and run the following command in the Package Manager Console (PMC):

Drop-Database
  • 重新啟動應用程式。Restart the app.

  • 選取 Students 頁面來查看植入的資料。Select the Students page to see the seeded data.

檢視資料庫View the database

  • 從 Visual Studio 中的 View 功能表開啟 SQL Server 物件總管 (SSOX)。Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio.
  • 在 SSOX 中,選取 [(localdb)\MSSQLLocalDB] > [資料庫] > [SchoolContext-{GUID}]****。In SSOX, select (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. 資料庫名稱是以您稍早所提供的內容名稱加上虛線和 GUID 所產生。The database name is generated from the context name you provided earlier plus a dash and a GUID.
  • 展開 Tables 節點。Expand the Tables node.
  • 以滑鼠右鍵按一下 Students 資料表,並按一下 [檢視資料]**** 查看建立的資料行、插入資料表中的資料列。Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.
  • 以滑鼠右鍵按一下 Student 資料表然後按一下 [檢視程式碼]**** 來查看 Student 模型對應到 Student 資料表結構描述的方式。Right-click the Student table and click View Code to see how the Student model maps to the Student table schema.

非同步程式碼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 can handle more traffic without delays.

非同步程式碼會在執行階段導致少量的額外負荷。Asynchronous code does introduce a small amount of overhead at run time. 在低流量情況下,對效能的衝擊非常微小;在高流量情況下,潛在的效能改善則相當大。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 OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • async 關鍵字會指示編譯器:The async keyword tells the compiler to:
    • 為方法主體的組件產生回呼。Generate callbacks for parts of the method body.
    • 建立傳回的 Task 物件。Create the Task object that's returned.
  • Task<T> 傳回型別代表正在進行的工作。The Task<T> return type represents ongoing work.
  • 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 Core 的非同步程式碼,請注意下列事項:Some things to be aware of when writing asynchronous code that uses EF Core:

  • 只有讓查詢或命令傳送至資料庫的陳述式,會以非同步方式執行。Only statements that cause queries or commands to be sent to the database are executed asynchronously. 其中包含 ToListAsyncSingleOrDefaultAsyncFirstOrDefaultAsyncSaveChangesAsyncThat includes ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. 不會包含只變更 IQueryable 的陳述式,例如 var students = context.Students.Where(s => s.LastName == "Davolio")It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • EF Core 內容在執行緒中並不安全:不要嘗試執行多個平行作業。An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
  • 若要利用非同步程式碼所帶來的效能利益,請驗證該程式庫套件 (例如用於分頁) 在呼叫傳送查詢到資料庫的 EF Core 方法時使用 async。To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the database.

如需非同步方法的詳細資訊,請參閱 Async 概觀使用 Async 和 Await 設計非同步程式For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.

後續步驟Next steps

Contoso 大學範例 web 應用程式示範如何 Razor 使用 Entity Framework (EF) Core 來建立 ASP.NET Core 頁面應用程式。The Contoso University sample web app demonstrates how to create an ASP.NET Core Razor Pages app using Entity Framework (EF) Core.

這個範例應用程式是虛構的 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 page is the first in a series of tutorials that explain how to build the Contoso University sample app.

下載或檢視已完成的應用程式。Download or view the completed app. 下載指示Download instructions.

PrerequisitesPrerequisites

包含下列工作負載的 Visual Studio 2019Visual Studio 2019 with the following workloads:

  • ASP.NET 和 Web 開發ASP.NET and web development
  • .NET Core 跨平台開發.NET Core cross-platform development

熟悉 Razor 頁面Familiarity with Razor Pages. 新的程式設計人員應該在開始本系列之前,先完成 Razor 頁面的入門New programmers should complete Get started with Razor Pages before starting this series.

疑難排解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. 取得協助的好方法是將問題公佈到 StackOverflow.com 以詢問 ASP.NET CoreEF CoreA good way to get help is by posting a question to StackOverflow.com for ASP.NET Core or EF Core.

Contoso 大學 Web 應用程式The 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 created in the tutorial.

Students [索引] 頁面

Students [編輯] 頁面

此網站的 UI 樣式接近內建範本所產生的內容。The UI style of this site is close to what's generated by the built-in templates. 本教學課程著重于 EF Core Razor 頁面,而不是 UI。The tutorial focus is on EF Core with Razor Pages, not the UI.

建立 ContosoUniversity Razor 頁面 web 應用程式Create the ContosoUniversity Razor Pages web app

  • 從 Visual Studio 的 [檔案]**** 功能表中,選取 [新增][專案] > **** 。From the Visual Studio File menu, select New > Project.
  • 建立新的 ASP.NET Core Web 應用程式。Create a new ASP.NET Core Web Application. 將專案命名為 ContosoUniversityName the project ContosoUniversity. 請務必將專案命名為 ContosoUniversity,當您複製/貼上程式碼時,命名空間才會相符。It's important to name the project ContosoUniversity so the namespaces match when code is copy/pasted.
  • 在下拉式清單中選取 [ASP.NET Core 2.1]****,然後選取 [Web 應用程式]****。Select ASP.NET Core 2.1 in the dropdown, and then select Web Application.

如需上述步驟的影像,請參閱 建立 Razor web 應用程式For images of the preceding steps, see Create a Razor web app. 執行應用程式。Run the app.

設定網站樣式Set up the site style

一些變更設定了網站的功能表、配置和首頁。A few changes set up the site menu, layout, and home page. 使用下列變更更新 Pages/Shared/_Layout.cshtmlUpdate Pages/Shared/_Layout.cshtml with the following changes:

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

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

所做的變更已醒目提示。The changes are highlighted. (所有標記都「不會」** 顯示。)(All the markup is not displayed.)

<!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" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/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" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-page="/Index" class="navbar-brand">Contoso University</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Students/Index">Students</a></li>
                    <li><a asp-page="/Courses/Index">Courses</a></li>
                    <li><a asp-page="/Instructors/Index">Instructors</a></li>
                    <li><a asp-page="/Departments/Index">Departments</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 : Contoso University</p>
        </footer>
    </div>

    @*Remaining markup not shown for brevity.*@

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

@page
@model IndexModel
@{
    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 Razor Pages web app.
        </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.microsoft.com/aspnet/core/data/ef-rp/intro">
                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/master/aspnetcore/data/ef-rp/intro/samples/">
                See project source code &raquo;
            </a>
        </p>
    </div>
</div>

建立資料模型Create the data model

為 Contoso 大學應用程式建立實體類別。Create entity classes for the Contoso University app. 從下列三個實體開始:Start with the following three entities:

Course-Enrollment-Student 資料模型圖表

StudentEnrollment 實體之間會有一對多關聯性。There's a one-to-many relationship between Student and Enrollment entities. CourseEnrollment 實體之間會有一對多關聯性。There's a one-to-many relationship between Course and Enrollment entities. 一位學生可註冊任何數目的課程。A student can enroll in any number of courses. 一堂課程可以讓任意數目的學生註冊。A course can have any number of students enrolled in it.

在下列一節中,將為這些實體建立各自的類別。In the following sections, a class for each one of these entities is created.

Student 實體The Student entity

Student 實體圖表

建立 Models 資料夾。Create a Models folder. Models 資料夾中,用下列程式碼建立一個名為 Student.cs 的類別檔案:In the Models folder, create a class file named Student.cs 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 屬性會成為資料庫 (DB) 資料表中的主索引鍵資料行,並對應至這個類別。The ID property becomes the primary key column of the database (DB) table that corresponds to this class. EF Core 預設會解譯名為 IDclassnameID 作為主索引鍵的屬性。By default, EF Core interprets a property that's named ID or classnameID as the primary key. classnameID 中,classname 是類別的名稱。In classnameID, classname is the name of the class. 另一個自動識別的主索引鍵是上述範例中的 StudentIDThe alternative automatically recognized primary key is StudentID in the preceding example.

Enrollments 屬性為導覽屬性The Enrollments property is a navigation property. 導覽屬性會連結至與此實體相關的其他實體。Navigation properties link to other entities that are related to this entity. 在這個案例中,Student entity 中的 Enrollments 屬性會保有與 Student 相關的所有 Enrollment 實體。In this case, the Enrollments property of a Student entity holds all of the Enrollment entities that are related to that Student. 例如,如果資料庫中 Student 資料列有兩個相關的 Enrollment 資料列,則 Enrollments 導覽屬性會包含這兩個 Enrollment 實體。For example, if a Student row in the DB has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities. 相關的 Enrollment 資料列,包含該學生在 StudentID 資料行中的主索引鍵值。A related Enrollment row is a row that contains that student's primary key value in the StudentID column. 例如,假設 student with ID=1 在 Enrollment 資料表中有兩個資料列。For example, suppose the student with ID=1 has two rows in the Enrollment table. Enrollment 資料表有兩個包含 StudentID=1 的資料列。The Enrollment table has two rows with StudentID = 1. StudentIDEnrollment 資料表中的外部索引鍵,會指定在 Student 資料表中的學生。StudentID is a foreign key in the Enrollment table that specifies the student in the Student table.

如果導覽屬性可以保存多個實體,該導覽屬性必然是清單類型,例如 ICollection<T>If a navigation property can hold multiple entities, the navigation property must be a list type, such as ICollection<T>. 您可以指定 ICollection<T>,或是如 List<T>HashSet<T> 的類型。ICollection<T> can be specified, or a type such as List<T> or HashSet<T>. 如使用 ICollection<T>,EF Core 預設將建立 HashSet<T> 集合。When ICollection<T> is used, EF Core creates a HashSet<T> collection by default. 保存多個實體的導覽屬性,來自多對多和一對多關聯性。Navigation properties that hold multiple entities come from many-to-many and one-to-many relationships.

Enrollment 實體The Enrollment entity

Enrollment 實體圖表

Models 資料夾中,用下列程式碼建立 Enrollment.csIn the Models folder, create Enrollment.cs 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 屬性是主索引鍵。The EnrollmentID property is the primary key. 這個實體會使用 classnameID 模式,而不是像 Student 實體一樣的 IDThis entity uses the classnameID pattern instead of ID like the Student entity. 開發人員通常會選擇其中一個模式,並使用於整個資料模型。Typically developers choose one pattern and use it throughout the data model. 在稍後的教學課程中,將示範使用不具類別名稱的 ID,使得在數據模型中實作繼承更加簡單。In a later tutorial, using ID without classname is shown to make 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 實體。An Enrollment entity is associated with one Student entity, so the property contains a single Student entity. Student 實體與 Student.Enrollments 導覽屬性不同,導覽屬性包含多個 Enrollment 實體。The Student entity differs from the Student.Enrollments navigation property, which contains 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.

如果實體名為 <navigation property name><primary key property name>,則 EF Core 會將之解釋為外部索引鍵。EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>. 例如,StudentIDStudent 導覽屬性,因為 Student 實體的主索引鍵是 IDFor example,StudentID for the Student navigation property, since the Student entity's primary key is ID. 外部索引鍵屬性也可命名為 <primary key property name>Foreign key properties can also be named <primary key property name>. 例如 CourseID,因為 Course 實體的主索引鍵是 CourseIDFor example, CourseID since the Course entity's primary key is CourseID.

Course 實體The Course entity

Course 實體圖表

Models 資料夾中,用下列程式碼建立 Course.csIn the Models folder, create Course.cs 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 屬性 (attribute) 可讓應用程式指定主索引鍵,而不是讓 DB 來產生。The DatabaseGenerated attribute allows the app to specify the primary key rather than having the DB generate it.

Scaffold 學生模型Scaffold the student model

在本節中會 scaffold 學生模型。In this section, the student model is scaffolded. 亦即 Scaffolding 工具會產生學生模型的建立、讀取、更新和刪除 (CRUD) 作業頁面。That is, the scaffolding tool produces pages for Create, Read, Update, and Delete (CRUD) operations for the student model.

  • 建置專案。Build the project.
  • 建立 Pages/Students 資料夾。Create the Pages/Students folder.
  • 在 [方案總管]**** 中,以滑鼠右鍵按一下 Pages/Students 資料夾 > [新增]** [新增 Scaffold 項目]** > ****。In Solution Explorer, right click on the Pages/Students folder > Add > New Scaffolded Item.
  • 在 [新增 Scaffold ] 對話方塊中, ** Razor 使用 Entity Framework (CRUD) **新增] 選取 [頁面] > ** **。In the Add Scaffold dialog, select Razor Pages using Entity Framework (CRUD) > ADD.

** Razor 使用 ENTITY FRAMEWORK (CRUD) 對話方塊來完成 [新增頁面**]:Complete the Add Razor Pages using Entity Framework (CRUD) dialog:

  • 在 [模型類別]**** 下拉式清單中,選取 [學生 (ContosoUniversity.Models)]****。In the Model class drop-down, select Student (ContosoUniversity.Models).
  • 在 [資料內容類別]**** 列中,選取 + (加號) 符號,並將產生的名稱變更為 ContosoUniversity.Models.SchoolContextIn the Data context class row, select the + (plus) sign and change the generated name to ContosoUniversity.Models.SchoolContext.
  • 在 [資料內容類別]**** 下拉式清單中,選取 ContosoUniversity.Models.SchoolContextIn the Data context class drop-down, select ContosoUniversity.Models.SchoolContext
  • 選取 [新增]。Select Add.

CRUD 對話方塊

如果您有上一個步驟的問題,請參閱 Scaffold 影片模型See Scaffold the movie model if you have a problem with the preceding step.

隨即建立 Scaffold 處理序並變更下列檔案:The scaffold process created and changed the following files:

建立的檔案Files created

  • Pages/Students 建立、刪除、詳細資料、編輯、索引。Pages/Students Create, Delete, Details, Edit, Index.
  • Data/SchoolContext.csData/SchoolContext.cs

檔案更新File updates

  • Startup.cs:這個檔案的變更會於下一節詳述。Startup.cs : Changes to this file are detailed in the next section.
  • appsettings.json:已新增用來連線到本機資料庫的連接字串。appsettings.json : The connection string used to connect to a local database is added.

檢查使用相依性插入所註冊的內容Examine the context registered with dependency injection

ASP.NET Core 內建相依性插入ASP.NET Core is built with dependency injection. 服務 (例如 EF Core DB 內容) 是在應用程式啟動期間使用相依性插入來註冊。Services (such as the EF Core DB context) are registered with dependency injection during application startup. 需要這些服務的元件 (例如 Razor 頁面) 是透過函式參數提供這些服務。Components that require these services (such as Razor Pages) are provided these services via constructor parameters. 取得資料庫內容執行個體的建構函式程式碼,在本教學課程稍後會示範。The constructor code that gets a db context instance is shown later in the tutorial.

Scaffolding 工具會自動建立資料庫內容,並向相依性插入容器註冊。The scaffolding tool automatically created a DB Context and registered it with the dependency injection container.

檢查 Startup.cs 中的 ConfigureServices 方法。Examine the ConfigureServices method in Startup.cs. 強調顯示的行由 Scaffolder 新增:The highlighted line was added by the scaffolder:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for 
        //non -essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

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

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

更新 mainUpdate main

Program.cs 中,修改 Main 方法來執行下列動作:In Program.cs, modify the Main method to do the following:

  • 從相依性插入容器中取得資料庫內容執行個體。Get a DB context instance from the dependency injection container.
  • 呼叫 EnsureCreatedCall the EnsureCreated.
  • EnsureCreated 方法完成時處理內容。Dispose the context when the EnsureCreated method completes.

下列程式碼顯示已更新的 Program.cs 檔案。The following code shows the updated Program.cs file.

using ContosoUniversity.Models;                   // SchoolContext
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;   // CreateScope
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        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>();
                    context.Database.EnsureCreated();
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }

            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

EnsureCreated 可確保內容的資料庫存在。EnsureCreated ensures that the database for the context exists. 如果存在,不會採取任何動作。If it exists, no action is taken. 如果不存在,則會建立資料庫及其所有結構描述。If it does not exist, then the database and all its schema are created. EnsureCreated 不會使用移轉來建立資料庫。EnsureCreated does not use migrations to create the database. 使用 EnsureCreated 建立的資料庫稍後無法使用移轉來加以更新。A database that is created with EnsureCreated cannot be later updated using migrations.

應用程式啟動時會呼叫 EnsureCreated 以允許下列工作流程:EnsureCreated is called on app start, which allows the following work flow:

  • 刪除資料庫。Delete the DB.
  • 變更資料庫結構描述 (例如新增 EmailAddress 欄位)。Change the DB schema (for example, add an EmailAddress field).
  • 執行應用程式。Run the app.
  • EnsureCreated 會建立包含 EmailAddress 資料行的資料庫。EnsureCreated creates a DB with theEmailAddress column.

在開發初期,結構描述快速發展之時,EnsureCreated 很方便。EnsureCreated is convenient early in development when the schema is rapidly evolving. 稍後在本教學課程中,會刪除資料庫並使用移轉。Later in the tutorial the DB is deleted and migrations are used.

測試應用程式Test the app

執行應用程式並接受 cookie 原則。Run the app and accept the cookie policy. 此應用程式不會保留個人資訊。This app doesn't keep personal information. 您可以參閱 EU 的 cookie 原則 一般資料保護規定 (GDPR) 支援人員You can read about the cookie policy at EU General Data Protection Regulation (GDPR) support.

  • 選取 [學生]**** 連結,然後選取 [新建]****。Select the Students link and then Create New.
  • 測試 [編輯]、[詳細資料] 和 [刪除] 連結。Test the Edit, Details, and Delete links.

檢查 SchoolContext 資料庫內容Examine the SchoolContext DB context

為給定的資料模型協調 EF Core 功能的主要類別是資料庫內容類別。The main class that coordinates EF Core functionality for a given data model is the DB context class. 資料內容衍生自 Microsoft.EntityFrameworkCore.DbContextThe data context is derived from Microsoft.EntityFrameworkCore.DbContext. 資料內容會指定資料模型包含哪些實體。The data context specifies which entities are included in the data model. 在此專案中,類別命名為 SchoolContextIn this project, the class is named SchoolContext.

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

using Microsoft.EntityFrameworkCore;

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

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

反白顯示的程式碼會為每個實體集建立DbSet <TEntity> 屬性。The highlighted code creates a DbSet<TEntity> property for each entity set. 在 EF Core 用語中:In EF Core terminology:

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

DbSet<Enrollment>DbSet<Course> 可加以忽略。DbSet<Enrollment> and DbSet<Course> could be omitted. EF Core 會隱含它們,因為 Student 實體引用 Enrollment 實體,而 Enrollment 實體引用 Course 實體。EF Core includes them implicitly because the Student entity references the Enrollment entity, and the Enrollment entity references the Course entity. 本教學課程中,請在 SchoolContext 保留 DbSet<Enrollment>DbSet<Course>For this tutorial, keep DbSet<Enrollment> and DbSet<Course> in the SchoolContext.

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.

新增程式碼來初始化含有測試資料的資料庫Add code to initialize the DB with test data

EF Core 會建立空白資料庫。EF Core creates an empty DB. 在本節中,會寫入 Initialize 方法並以測試資料來填入該資料庫。In this section, an Initialize method is written to populate it with test data.

Data 資料夾中,建立名為 DbInitializer.cs 的新類別檔案並新增下列程式碼:In the Data folder, create a new class file named DbInitializer.cs and add the following code:

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

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

            // Look for any students.
            if (context.Student.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.Student.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.Course.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.Enrollment.Add(e);
            }
            context.SaveChanges();
        }
    }
}

注意:上述程式碼會使用做 Models 為命名空間 (namespace ContosoUniversity.Models) 而不是 DataNote: The preceding code uses Models for the namespace (namespace ContosoUniversity.Models) rather than Data. Models 與產生框架的程式碼一致。Models is consistent with the scaffolder-generated code. 如需詳細資訊,請參閱此 GitHub Scaffolding 問題For more information, see this GitHub scaffolding issue.

程式碼會檢查資料庫中是否有任何學生。The code checks if there are any students in the DB. 如果資料庫中沒有學生,則會使用測試資料初始化資料庫。If there are no students in the DB, the DB is initialized with test data. 會將測試資料載入至陣列而非 List<T> 集合,以最佳化效能。It loads test data into arrays rather than List<T> collections to optimize performance.

EnsureCreated 方法會自動為資料庫內容建立資料庫。The EnsureCreated method automatically creates the DB for the DB context. 如果資料庫已存在,則 EnsureCreated 將會傳回而不修改該資料庫。If the DB exists, EnsureCreated returns without modifying the DB.

Program.cs 中,修改 Main 方法來呼叫 InitializeIn Program.cs, modify the Main method to call Initialize:

public class Program
{
    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>();
                // using ContosoUniversity.Data; 
                DbInitializer.Initialize(context);
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred creating the DB.");
            }
        }

        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

停止應用程式 (如果它正在執行),並在套件管理員主控台 (PMC) 中執行下列命令:Stop the app if it's running, and run the following command in the Package Manager Console (PMC):

Drop-Database

檢視資料庫View the DB

資料庫名稱是以您稍早所提供的內容名稱加上虛線和 GUID 所產生。The database name is generated from the context name you provided earlier plus a dash and a GUID. 因此,資料庫名稱將會是 "SchoolContext-{GUID}"。Thus, the database name will be "SchoolContext-{GUID}". GUID 針對每個使用者都將不同。The GUID will be different for each user. 從 Visual Studio 中的 View 功能表開啟 SQL Server 物件總管 (SSOX)。Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio. 在 SSOX 中,按一下 (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}In SSOX, click (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}.

展開 Tables 節點。Expand the Tables node.

以滑鼠右鍵按一下 Students 資料表,並按一下 [檢視資料]**** 查看建立的資料行、插入資料表中的資料列。Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.

非同步程式碼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. 在低流量情況下,對效能的衝擊非常微小;在高流量情況下,潛在的效能改善則相當大。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 OnGetAsync()
{
    Student = await _context.Student.ToListAsync();
}
  • async 關鍵字會指示編譯器:The async keyword tells the compiler to:

    • 為方法主體的組件產生回呼。Generate callbacks for parts of the method body.
    • 自動建立傳回的 Task 物件。Automatically create the Task object that's returned. 如需詳細資訊,請參閱 Task 傳回型別For more information, see Task Return Type.
  • 隱含傳回型別 Task 代表進行中的工作。The implicit return type Task represents ongoing work.

  • 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 Core 的非同步程式碼,請注意下列事項:Some things to be aware of when writing asynchronous code that uses EF Core:

  • 只有讓查詢或命令傳送至資料庫的陳述式,會以非同步方式執行。Only statements that cause queries or commands to be sent to the DB are executed asynchronously. 包含 ToListAsyncSingleOrDefaultAsyncFirstOrDefaultAsyncSaveChangesAsyncThat includes, ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. 不會包含只變更 IQueryable 的陳述式,例如 var students = context.Students.Where(s => s.LastName == "Davolio")It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • EF Core 內容在執行緒中並不安全:不要嘗試執行多個平行作業。An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
  • 若要利用非同步程式碼的效能優勢,請確認程式庫套件 (例如分頁) 在呼叫將查詢傳送至資料庫的 EF Core 方法時是使用非同步。To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the DB.

如需非同步方法的詳細資訊,請參閱 Async 概觀使用 Async 和 Await 設計非同步程式For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.

在下一個教學課程中,將會檢視基本的 CRUD (建立、讀取、更新、刪除) 作業。In the next tutorial, basic CRUD (create, read, update, delete) operations are examined.

其他資源Additional resources