ASP.NET Core 中的 Razor 頁(yè)面和 Entity Framework Core

2019-04-15 12:59 更新

Contoso University 示例 Web 應(yīng)用演示了如何使用 Entity Framework (EF) Core 創(chuàng)建 ASP.NET Core Razor Pages 應(yīng)用。

該示例應(yīng)用是一個(gè)虛構(gòu)的 Contoso University 的網(wǎng)站。 其中包括學(xué)生錄取、課程創(chuàng)建和講師分配等功能。 本頁(yè)是介紹如何構(gòu)建 Contoso University 示例應(yīng)用系列教程中的第一部分。

下載或查看已完成的應(yīng)用。 下載說明。

系統(tǒng)必備

具有以下工作負(fù)載的 Visual Studio 2017 15.7.3 版或更高版本

  • ASP.NET 和 Web 開發(fā)
  • .NET Core 跨平臺(tái)開發(fā)

熟悉 Razor 頁(yè)面。 新程序員在開始學(xué)習(xí)本系列之前,應(yīng)先完成 Razor 頁(yè)面入門。

疑難解答

如果遇到無法解決的問題,可以通過與 已完成的項(xiàng)目對(duì)比代碼來查找解決方案。 獲取幫助的一個(gè)好方法是將問題發(fā)布到適用于 ASP.NET Core 或 EF Core 的 StackOverflow.com。

Contoso University Web 應(yīng)用

這些教程中所構(gòu)建的應(yīng)用是一個(gè)基本的大學(xué)網(wǎng)站。

用戶可以查看和更新學(xué)生、課程和講師信息。 以下是在本教程中創(chuàng)建的幾個(gè)屏幕。

“學(xué)生索引”頁(yè)

學(xué)生編輯頁(yè)

此網(wǎng)站的 UI 樣式與內(nèi)置模板生成的 UI 樣式類似。 教程的重點(diǎn)是 EF Core 和 Razor 頁(yè)面,而非 UI。

創(chuàng)建 ContosoUniversity Razor Pages Web 應(yīng)用

  • 從 Visual Studio“文件”菜單中,選擇“新建”>“項(xiàng)目”。
  • 創(chuàng)建新的 ASP.NET Core Web 應(yīng)用程序。 將該項(xiàng)目命名為 ContosoUniversity 。 務(wù)必將該項(xiàng)目命名為 ContosoUniversity,以便復(fù)制/粘貼代碼時(shí)命名空間相匹配。
  • 在下拉列表中選擇“ASP.NET Core 2.1”,然后選擇“Web 應(yīng)用程序”。

有關(guān)上述步驟的圖像,請(qǐng)參閱創(chuàng)建 Razor Web 應(yīng)用。 運(yùn)行應(yīng)用。

設(shè)置網(wǎng)站樣式

設(shè)置網(wǎng)站菜單、布局和主頁(yè)時(shí)需作少量更改。 進(jìn)行以下更改以更新 Pages/Shared/_Layout.cshtml:

  • 將文件中的"ContosoUniversity"更改為"Contoso University"。 需要更改三個(gè)地方。
  • 添加菜單項(xiàng) Students,Courses,Instructors,和 Department,并刪除 Contact菜單項(xiàng)。

突出顯示所作更改。 (所有標(biāo)記均不顯示。)

HTML

<!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"  rel="external nofollow" target="_blank" 
              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 中,將文件內(nèi)容替換為以下代碼,以將有關(guān) ASP.NET 和 MVC 的文本替換為有關(guān)本應(yīng)用的文本:

HTML

@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"
                rel="external nofollow" target="_blank" >
                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="http://m.hgci.cn/targetlink?url=https://github.com/aspnet/Docs/tree/master/aspnetcore/data/ef-rp/intro/samples/cu-final">
                See project source code &raquo;
            </a>
        </p>
    </div>
</div>

創(chuàng)建數(shù)據(jù)模型

創(chuàng)建 Contoso University 應(yīng)用的實(shí)體類。 從以下三個(gè)實(shí)體開始:

Course-Enrollment-Student 數(shù)據(jù)模型關(guān)系圖

Student 和 Enrollment 實(shí)體之間存在一對(duì)多關(guān)系。 Course 和 Enrollment 實(shí)體之間存在一對(duì)多關(guān)系。 一名學(xué)生可以報(bào)名參加任意數(shù)量的課程。 一門課程中可以包含任意數(shù)量的學(xué)生。

以下部分將為這幾個(gè)實(shí)體中的每一個(gè)實(shí)體創(chuàng)建一個(gè)類。

Student 實(shí)體

Student 實(shí)體關(guān)系圖

創(chuàng)建 Models 文件夾。 在 Models 文件夾中,使用以下代碼創(chuàng)建一個(gè)名為 Student.cs 的類文件:

C#

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 屬性成為此類對(duì)應(yīng)的數(shù)據(jù)庫(kù) (DB) 表的主鍵列。 默認(rèn)情況下,EF Core 將名為 ID 或 classnameID 的屬性視為主鍵。 在 classnameID 中,classname 為類名稱。 另一種自動(dòng)識(shí)別的主鍵是上例中的 StudentID。

Enrollments 屬性是導(dǎo)航屬性。 導(dǎo)航屬性鏈接到與此實(shí)體相關(guān)的其他實(shí)體。 在這種情況下,Student entity 的 Enrollments 屬性包含與該 Student 相關(guān)的所有 Enrollment 實(shí)體。 例如,如果數(shù)據(jù)庫(kù)中的 Student 行有兩個(gè)相關(guān)的 Enrollment 行,則 Enrollments 導(dǎo)航屬性包含這兩個(gè) Enrollment 實(shí)體。 相關(guān)的 Enrollment 行是 StudentID 列中包含該學(xué)生的主鍵值的行。 例如,假設(shè) ID=1 的學(xué)生在 Enrollment 表中有兩行。 Enrollment 表中有兩行的 StudentID = 1。 StudentID 是 Enrollment 表中的外鍵,用于指定 Student 表中的學(xué)生。

如果導(dǎo)航屬性包含多個(gè)實(shí)體,則導(dǎo)航屬性必須是列表類型,例如 ICollection<T>。 可以指定 ICollection<T> 或諸如 List<T> 或 HashSet<T> 的類型。 使用 ICollection<T> 時(shí),EF Core 會(huì)默認(rèn)創(chuàng)建 HashSet<T> 集合。 包含多個(gè)實(shí)體的導(dǎo)航屬性來自于多對(duì)多和一對(duì)多關(guān)系。

Enrollment 實(shí)體

Enrollment 實(shí)體關(guān)系圖

在 Models 文件夾中,使用以下代碼創(chuàng)建 Enrollment.cs:

C#

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 屬性為主鍵。 Student 實(shí)體使用的是 ID 模式,而本實(shí)體使用的是 classnameID 模式。 通常情況下,開發(fā)者會(huì)選擇一種模式并在整個(gè)數(shù)據(jù)模型中都使用該模式。 下一個(gè)教程將介紹如何使用不帶類名的 ID,以便更輕松地在數(shù)據(jù)模型中實(shí)現(xiàn)集成。

Grade 屬性為 enum。 Grade 聲明類型后的?表示 Grade 屬性可以為 null。 評(píng)級(jí)為 null 和評(píng)級(jí)為零是有區(qū)別的 --null 意味著評(píng)級(jí)未知或者尚未分配。

StudentID 屬性是外鍵,其對(duì)應(yīng)的導(dǎo)航屬性為 Student。 Enrollment 實(shí)體與一個(gè) Student 實(shí)體相關(guān)聯(lián),因此該屬性只包含一個(gè) Student 實(shí)體。 Student 實(shí)體與 Student.Enrollments 導(dǎo)航屬性不同,后者包含多個(gè) Enrollment 實(shí)體。

CourseID 屬性是外鍵,其對(duì)應(yīng)的導(dǎo)航屬性為 Course。 Enrollment 實(shí)體與一個(gè) Course 實(shí)體相關(guān)聯(lián)。

如果屬性命名為 <navigation property name><primary key property name>,EF Core 會(huì)將其視為外鍵。例如 Student 導(dǎo)航屬性的 StudentID,因?yàn)?nbsp;Student 實(shí)體的主鍵為 ID。 還可以將外鍵屬性命名為 <primary key property name>。 例如 CourseID,因?yàn)?nbsp;Course 實(shí)體的主鍵為 CourseID。

Course 實(shí)體

Course 實(shí)體關(guān)系圖

在 Models 文件夾中,使用以下代碼創(chuàng)建 Course.cs:

C#

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 屬性是導(dǎo)航屬性。 Course 實(shí)體可與任意數(shù)量的 Enrollment 實(shí)體相關(guān)。

應(yīng)用可以通過 DatabaseGenerated 特性指定主鍵,而無需靠數(shù)據(jù)庫(kù)生成。

為“學(xué)生”模型搭建基架

本部分將為“學(xué)生”模型搭建基架。 確切地說,基架工具將生成頁(yè)面,用于對(duì)“學(xué)生”模型執(zhí)行創(chuàng)建、讀取、更新和刪除 (CRUD) 操作。

  • 生成項(xiàng)目。
  • 創(chuàng)建 Pages/Students 文件夾。
  • 在“解決方案資源管理器”中,右鍵單擊“Pages/Students”文件夾>“添加”>“新搭建基架的項(xiàng)目”。
  • 在“添加基架”對(duì)話框中,選擇“使用實(shí)體框架生成 Razor Pages (CRUD)”>“添加”。

完成“使用實(shí)體框架(CRUD)添加 Razor Pages”對(duì)話框:

  • 在“模型類”下拉列表中,選擇“學(xué)生(ContosoUniversity.Models)”。
  • 在“數(shù)據(jù)上下文類”行中,選擇加號(hào) (+) 并將生成的名稱更改為 ContosoUniversity.Models.SchoolContext。
  • 在“數(shù)據(jù)上下文類”下拉列表中,選擇“ContosoUniversity.Models.SchoolContext”
  • 選擇“添加”。

CRUD 對(duì)話框

如果對(duì)前面的步驟有疑問,請(qǐng)參閱搭建“電影”模型的基架。

搭建基架的過程會(huì)創(chuàng)建并更改以下文件:

創(chuàng)建的文件

  • Pages/Students:“創(chuàng)建”、“刪除”、“詳細(xì)信息”、“編輯”、“索引”。
  • Data/SchoolContext.cs

文件更新

  • Startup.cs:下一部分詳細(xì)介紹對(duì)此文件所作的更改。
  • appsettings.json:添加用于連接到本地?cái)?shù)據(jù)庫(kù)的連接字符串。

檢查通過依賴關(guān)系注入注冊(cè)的上下文

ASP.NET Core 通過依賴關(guān)系注入進(jìn)行生成。 服務(wù)(例如 EF Core 數(shù)據(jù)庫(kù)上下文)在應(yīng)用程序啟動(dòng)期間通過依賴關(guān)系注入進(jìn)行注冊(cè)。 需要這些服務(wù)(如 Razor 頁(yè)面)的組件通過構(gòu)造函數(shù)提供相應(yīng)服務(wù)。 本教程的后續(xù)部分介紹了用于獲取數(shù)據(jù)庫(kù)上下文實(shí)例的構(gòu)造函數(shù)代碼。

基架工具自動(dòng)創(chuàng)建 DB 上下文并將其注冊(cè)到依賴關(guān)系注入容器。

在 Startup.cs 中檢查 ConfigureServices 方法。 基架添加了突出顯示的行:

C#

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")));
}

通過調(diào)用 DbContextOptions 對(duì)象中的一個(gè)方法將連接字符串名稱傳遞到上下文。 進(jìn)行本地開發(fā)時(shí), ASP.NET Core 配置系統(tǒng) 在 appsettings.json 文件中讀取數(shù)據(jù)庫(kù)連接字符串。

更新 main

在 Program.cs 中,修改 Main 方法以執(zhí)行以下操作:

  • 從依賴關(guān)系注入容器獲取數(shù)據(jù)庫(kù)上下文實(shí)例。
  • 調(diào)用 EnsureCreated。
  • EnsureCreated 方法完成時(shí)釋放上下文。

下面的代碼顯示更新后的 Program.cs 文件。

C#

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 確保存在上下文數(shù)據(jù)庫(kù)。 如果存在,則不需要任何操作。 如果不存在,則會(huì)創(chuàng)建數(shù)據(jù)庫(kù)及其所有架構(gòu)。 EnsureCreated 不使用遷移創(chuàng)建數(shù)據(jù)庫(kù)。 使用 EnsureCreated 創(chuàng)建的數(shù)據(jù)庫(kù)稍后無法使用遷移更新。

啟動(dòng)應(yīng)用時(shí)會(huì)調(diào)用 EnsureCreated,以進(jìn)行以下工作流:

  • 刪除數(shù)據(jù)庫(kù)。
  • 更改數(shù)據(jù)庫(kù)架構(gòu)(例如添加一個(gè) EmailAddress 字段)。
  • 運(yùn)行應(yīng)用。
  • EnsureCreated 創(chuàng)建一個(gè)帶有 EmailAddress 列的數(shù)據(jù)庫(kù)。

架構(gòu)快速演變時(shí),在開發(fā)初期使用 EnsureCreated 很方便。 本教程后面將刪除 DB 并使用遷移。

測(cè)試應(yīng)用

運(yùn)行應(yīng)用并接受 cookie 策略。 此應(yīng)用不保留個(gè)人信息。 有關(guān) cookie 策略的信息,請(qǐng)參閱歐盟一般數(shù)據(jù)保護(hù)條例 (GDPR) 支持。

  • 依次選擇“學(xué)生”鏈接、“新建”。
  • 測(cè)試“編輯”、“詳細(xì)信息”和“刪除”鏈接。

檢查 SchoolContext DB 上下文

數(shù)據(jù)庫(kù)上下文類是為給定數(shù)據(jù)模型協(xié)調(diào) EF Core 功能的主類。 數(shù)據(jù)上下文派生自 Microsoft.EntityFrameworkCore.DbContext。 數(shù)據(jù)上下文指定數(shù)據(jù)模型中包含哪些實(shí)體。 在此項(xiàng)目中將數(shù)據(jù)庫(kù)上下文類命名為 SchoolContext。

使用以下代碼更新 SchoolContext.cs:

C#

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; }
    }
}

突出顯示的代碼為每個(gè)實(shí)體集創(chuàng)建 DbSet<TEntity> 屬性。 在 EF Core 術(shù)語(yǔ)中:

  • 實(shí)體集通常對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)表。
  • 實(shí)體對(duì)應(yīng)表中的行。

可以省略 DbSet<Enrollment> 和 DbSet<Course>。 EF Core 隱式包含了它們,因?yàn)?nbsp;Student 實(shí)體引用 Enrollment 實(shí)體,而 Enrollment 實(shí)體引用 Course 實(shí)體。 在本教程中,將 DbSet<Enrollment>和 DbSet<Course> 保留在 SchoolContext 中。

SQL Server Express LocalDB

連接字符串指定 SQL Server LocalDB。 LocalDB 是輕型版本 SQL Server Express 數(shù)據(jù)庫(kù)引擎,專門針對(duì)應(yīng)用開發(fā),而非生產(chǎn)使用。 LocalDB 作為按需啟動(dòng)并在用戶模式下運(yùn)行的輕量級(jí)數(shù)據(jù)庫(kù)沒有復(fù)雜的配置。 默認(rèn)情況下,LocalDB 會(huì)在 C:/Users/<user> 目錄中創(chuàng)建 .mdf 數(shù)據(jù)庫(kù)文件。

添加代碼,以使用測(cè)試數(shù)據(jù)初始化該數(shù)據(jù)庫(kù)

EF Core 會(huì)創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù)。 本部分中編寫了 Initialize 方法來使用測(cè)試數(shù)據(jù)填充該數(shù)據(jù)庫(kù)。

在 Data 文件夾中,新建一個(gè)名為 DbInitializer.cs 的類文件,并添加以下代碼:

C#

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();
        }
    }
}

注意:上面的代碼對(duì)命名空間使用 Models (namespace ContosoUniversity.Models),而不是 Data。 Models 與基架生成的代碼一致。 有關(guān)詳細(xì)信息,請(qǐng)參閱此 GitHub 基架問題

該代碼會(huì)檢查數(shù)據(jù)庫(kù)中是否存在任何學(xué)生。 如果 DB 中沒有任何學(xué)生,則會(huì)使用測(cè)試數(shù)據(jù)初始化該 DB。 代碼中使用數(shù)組存放測(cè)試數(shù)據(jù)而不是使用 List<T> 集合是為了優(yōu)化性能。

EnsureCreated 方法自動(dòng)為數(shù)據(jù)庫(kù)上下文創(chuàng)建數(shù)據(jù)庫(kù)。 如果數(shù)據(jù)庫(kù)已存在,則返回 EnsureCreated,并且不修改數(shù)據(jù)庫(kù)。

在 Program.cs 中,將 Main 方法修改為調(diào)用 Initialize:

C#

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>();
}

刪除任何學(xué)生記錄并重啟應(yīng)用。 如果未初始化 DB,則在 Initialize 中設(shè)置斷點(diǎn)以診斷問題。

查看數(shù)據(jù)庫(kù)

從 Visual Studio 中的“視圖”菜單打開 SQL Server 對(duì)象資源管理器 (SSOX)。 在 SSOX 中,單擊“(localdb)\MSSQLLocalDB”>“數(shù)據(jù)庫(kù)”>“ContosoUniversity1”。

展開“表”節(jié)點(diǎn)。

右鍵單擊 Student 表,然后單擊“查看數(shù)據(jù)”,以查看創(chuàng)建的列和插入到表中的行。

異步代碼

異步編程是 ASP.NET Core 和 EF Core 的默認(rèn)模式。

Web 服務(wù)器的可用線程是有限的,而在高負(fù)載情況下的可能所有線程都被占用。 當(dāng)發(fā)生這種情況的時(shí)候,服務(wù)器就無法處理新請(qǐng)求,直到線程被釋放。 使用同步代碼時(shí),可能會(huì)出現(xiàn)多個(gè)線程被占用但不能執(zhí)行任何操作的情況,因?yàn)樗鼈冋诘却?I/O 完成。 使用異步代碼時(shí),當(dāng)進(jìn)程正在等待 I/O 完成,服務(wù)器可以將其線程釋放用于處理其他請(qǐng)求。 因此,使用異步代碼可以更有效地利用服務(wù)器資源,并且可以讓服務(wù)器在沒有延遲的情況下處理更多流量。

異步代碼會(huì)在運(yùn)行時(shí)引入少量開銷。 流量較低時(shí),對(duì)性能的影響可以忽略不計(jì),但流量較高時(shí),潛在的性能改善非常顯著。

在以下代碼中,async 關(guān)鍵字和 Task<T> 返回值,await 關(guān)鍵字和 ToListAsync 方法讓代碼異步執(zhí)行。

C#

public async Task OnGetAsync()
{
    Student = await _context.Student.ToListAsync();
}
  • async 關(guān)鍵字讓編譯器執(zhí)行以下操作:為方法主體的各部分生成回調(diào)。自動(dòng)創(chuàng)建返回的 Task 對(duì)象。 有關(guān)詳細(xì)信息,請(qǐng)參閱任務(wù)返回類型。
  • 隱式返回類型 Task 表示正在進(jìn)行的工作。
  • await 關(guān)鍵字讓編譯器將該方法拆分為兩個(gè)部分。 第一部分是以異步方式結(jié)束已啟動(dòng)的操作。第二部分是當(dāng)操作完成時(shí)注入調(diào)用回調(diào)方法的地方。
  • ToListAsync 是 ToList 擴(kuò)展方法的異步版本。

編寫使用 EF Core 的異步代碼時(shí)需要注意的一些事項(xiàng):

  • 只會(huì)異步執(zhí)行導(dǎo)致查詢或命令被發(fā)送到數(shù)據(jù)庫(kù)的語(yǔ)句。 這包括 ToListAsync、SingleOrDefaultAsync、FirstOrDefaultAsync 和 SaveChangesAsync。 不包括只會(huì)更改 IQueryable 的語(yǔ)句,例如 var students = context.Students.Where(s => s.LastName == "Davolio")。
  • EF Core 上下文并非線程安全:請(qǐng)勿嘗試并行執(zhí)行多個(gè)操作。
  • 若要利用異步代碼的性能優(yōu)勢(shì),請(qǐng)驗(yàn)證在調(diào)用向數(shù)據(jù)庫(kù)發(fā)送查詢的 EF Core 方法時(shí),庫(kù)程序包(如用于分頁(yè))是否使用異步。

有關(guān) .NET 中異步編程的詳細(xì)信息,請(qǐng)參閱異步概述使用 Async 和 Await 的異步編程

下一個(gè)教程將介紹基本的 CRUD(創(chuàng)建、讀取、更新、刪除)操作。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)