ASP.NET Core 中的 Razor 頁面和 Entity Framework Core

2019-04-15 12:59 更新

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

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

下載或查看已完成的應用。 下載說明。

系統(tǒng)必備

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

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

熟悉 Razor 頁面。 新程序員在開始學習本系列之前,應先完成 Razor 頁面入門

疑難解答

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

Contoso University Web 應用

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

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

“學生索引”頁

學生編輯頁

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

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

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

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

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

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

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

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

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)本應用的文本:

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 應用的實體類。 從以下三個實體開始:

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

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

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

Student 實體

Student 實體關(guān)系圖

創(chuàng)建 Models 文件夾。 在 Models 文件夾中,使用以下代碼創(chuàng)建一個名為 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 屬性成為此類對應的數(shù)據(jù)庫 (DB) 表的主鍵列。 默認情況下,EF Core 將名為 ID 或 classnameID 的屬性視為主鍵。 在 classnameID 中,classname 為類名稱。 另一種自動識別的主鍵是上例中的 StudentID。

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

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

Enrollment 實體

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

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

StudentID 屬性是外鍵,其對應的導航屬性為 Student。 Enrollment 實體與一個 Student 實體相關(guān)聯(lián),因此該屬性只包含一個 Student 實體。 Student 實體與 Student.Enrollments 導航屬性不同,后者包含多個 Enrollment 實體。

CourseID 屬性是外鍵,其對應的導航屬性為 Course。 Enrollment 實體與一個 Course 實體相關(guān)聯(lián)。

如果屬性命名為 <navigation property name><primary key property name>,EF Core 會將其視為外鍵。例如 Student 導航屬性的 StudentID,因為 Student 實體的主鍵為 ID。 還可以將外鍵屬性命名為 <primary key property name>。 例如 CourseID,因為 Course 實體的主鍵為 CourseID。

Course 實體

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

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

為“學生”模型搭建基架

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

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

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

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

CRUD 對話框

如果對前面的步驟有疑問,請參閱搭建“電影”模型的基架。

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

創(chuàng)建的文件

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

文件更新

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

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

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

基架工具自動創(chuàng)建 DB 上下文并將其注冊到依賴關(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 對象中的一個方法將連接字符串名稱傳遞到上下文。 進行本地開發(fā)時, ASP.NET Core 配置系統(tǒng) 在 appsettings.json 文件中讀取數(shù)據(jù)庫連接字符串。

更新 main

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

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

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

啟動應用時會調(diào)用 EnsureCreated,以進行以下工作流:

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

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

測試應用

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

  • 依次選擇“學生”鏈接、“新建”。
  • 測試“編輯”、“詳細信息”和“刪除”鏈接。

檢查 SchoolContext DB 上下文

數(shù)據(jù)庫上下文類是為給定數(shù)據(jù)模型協(xié)調(diào) EF Core 功能的主類。 數(shù)據(jù)上下文派生自 Microsoft.EntityFrameworkCore.DbContext。 數(shù)據(jù)上下文指定數(shù)據(jù)模型中包含哪些實體。 在此項目中將數(shù)據(jù)庫上下文類命名為 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; }
    }
}

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

  • 實體集通常對應一個數(shù)據(jù)庫表。
  • 實體對應表中的行。

可以省略 DbSet<Enrollment> 和 DbSet<Course>。 EF Core 隱式包含了它們,因為 Student 實體引用 Enrollment 實體,而 Enrollment 實體引用 Course 實體。 在本教程中,將 DbSet<Enrollment>和 DbSet<Course> 保留在 SchoolContext 中。

SQL Server Express LocalDB

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

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

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

在 Data 文件夾中,新建一個名為 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();
        }
    }
}

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

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

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

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

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

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

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

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

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

異步代碼

異步編程是 ASP.NET Core 和 EF Core 的默認模式。

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

異步代碼會在運行時引入少量開銷。 流量較低時,對性能的影響可以忽略不計,但流量較高時,潛在的性能改善非常顯著。

在以下代碼中,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)。自動創(chuàng)建返回的 Task 對象。 有關(guān)詳細信息,請參閱任務返回類型
  • 隱式返回類型 Task 表示正在進行的工作。
  • await 關(guān)鍵字讓編譯器將該方法拆分為兩個部分。 第一部分是以異步方式結(jié)束已啟動的操作。第二部分是當操作完成時注入調(diào)用回調(diào)方法的地方。
  • ToListAsync 是 ToList 擴展方法的異步版本。

編寫使用 EF Core 的異步代碼時需要注意的一些事項:

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

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

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


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號