ASP.NET Core 和 EF Core 系列教程——入門

作者:Tom DykstraRick Anderson

此處提供了本教程的 Razor 頁版本。 Razor 頁版本更容易體現和覆蓋 EF 的功能。 我們建議你學習本教程的 Razor 頁版本。

Contoso 大學示例 web 應用程序演示如何使用 Entity Framework (EF) Core 2.0 和 Visual Studio 2017 創建 ASP.NET Core 2.0 MVC web 應用程序。

示例應用程序供一個虛構的 Contoso 大學網站使用。 它包括諸如學生入學、 課程創建和導師分配等功能。 這是一系列教程中的第一個,這一系列教程主要展示了如何從零開始構建 Contoso 大學示例應用程序。

下載或查看已完成的應用程序。

EF Core 2.0 是 EF 的最新版本,但還沒有包括 EF 6.x 的所有功能 。 有關如何在 EF 6.x 和 EF Core 之間選擇,請參閱EF Core vs. EF6.x。 如果你選擇使用 EF 6.x,請參閱本系列教程的上一個版本

準備

安裝以下組件:

  • .NET Core 2.0.0 SDK 或更高版本。
  • 已安裝 ASP.NET 和 Web 開發工作負載的 Visual Studio 2017 15.3 版或更高版本。

疑難解答

如果你遇到無法解決的問題,可以通過比較已完成的項目查找解決方案。常見錯誤以及對應的解決方案,請參閱最新教程中的故障排除。 如果沒有找到遇到的問題的解決方案你可以將問題發布到StackOverflow.com 的 ASP.NET CoreEF Core版塊。

這是一系列一共有十個教程,其中每個都是在前面教程已完成的基礎上繼續。請考慮在完成每一個教程后保存項目的副本。之后如果遇到問題,你可以從保存的副本中開始尋找問題,而不是從頭開始。

Contoso 大學 web 應用程序

你將在這些教程中學習構建一個簡單的大學網站的應用程序。

用戶可以查看和更新學生、 課程和教師信息。 以下是一些你即將創建的頁面。

學生索引頁
學生編輯頁

本教程主要關注于如何使用 Entity Framework , 所以此站點的UI樣式都是直接套用內置的模板。

創建 ASP.NET Core MVC web 應用程序

打開 Visual Studio 并創建一個新 ASP.NET Core C# web 項目名為”ContosoUniversity”。

  • 文件菜單上,選擇新建 > 項目。

  • 從左窗格中,選擇已安裝 > Visual C# > Web。

  • 選擇“ASP.NET Core Web 應用程序”項目模板。

  • 輸入ContosoUniversity作為名稱,然后單擊確定

    “新建項目”對話框
  • 等待新 ASP.NET Core Web 應用程序 (.NET Core)顯示對話框

  • 選擇ASP.NET Core 2.0Web 應用程序 (模型-視圖-控制器)模板。

    注意:本教程需要安裝 ASP.NET Core 2.0 和 EF Core 2.0 或更高版本-請確保ASP.NET Core 1.1未選中。

  • 請確保身份驗證設置為不進行身份驗證。

  • 單擊“確定”

    “新建 ASP.NET 項目”對話框

設置站點樣式

通過幾個簡單的更改設置站點菜單、 布局和主頁。

打開Views/Shared/_Layout.cshtml并進行以下更改:

  • 將文件中的”ContosoUniversity”更改為”Contoso University”。 需要更改三個地方。

  • 添加菜單項StudentsCourses,Instructors,和Department,并刪除Contact菜單項。

高亮代碼顯示所作的變化

<!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 names="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment names="Staging,Production">
        <link rel="stylesheet" 
              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-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">Contoso University</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
                    <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
                    <li><a asp-area="" asp-controller="Students" asp-action="Index">Students</a></li>
                    <li><a asp-area="" asp-controller="Courses" asp-action="Index">Courses</a></li>
                    <li><a asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a></li>
                    <li><a asp-area="" asp-controller="Departments" asp-action="Index">Departments</a></li>
                </ul>
            </div>
        </div>
    </nav>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2017 - Contoso University</p>
        </footer>
    </div>

    <environment names="Development">
        <script src="~/lib/jquery/dist/jquery.js"></script>
        <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
        <script src="~/js/site.js" asp-append-version="true"></script>
    </environment>
    <environment names="Staging,Production">
        <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
                asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                asp-fallback-test="window.jQuery"
                crossorigin="anonymous"
                integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
        </script>
        <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
                asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
                asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
                crossorigin="anonymous"
                integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
        </script>
        <script src="~/js/site.min.js" asp-append-version="true"></script>
    </environment>

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

Views/Home/Index.cshtml,將文件的內容替換為以下代碼以將有關 ASP.NET 和 MVC 的內容替換為有關此應用程序的內容:

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

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" >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" >See project source code &raquo;</a></p>
    </div>
</div>

按 CTRL + F5 來運行該項目或從菜單選擇調試 > 開始執行不調試。 你會看到首頁和將通過這個教程創建的頁對應的選項卡。

Contoso 大學主頁

Entity Framework Core NuGet 包

若要為項目添加 EF Core 支持,需要安裝相應的數據庫驅動包。 本教程使用 SQL Server,相關驅動包Microsoft.EntityFrameworkCore.SqlServer。 該包包含在Microsoft.AspNetCore.All 包中,因此不需要手動安裝。

此包和其依賴項 (Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.Relational) 一起提供 EF 的運行時支持。 你將在之后的遷移教程中學習添加工具包。

有關其他可用于 EF Core 的數據庫驅動的信息,請參閱數據庫驅動

創建數據模型

接下來你將創建 Contoso 大學應用程序的實體類。 你將從以下三個實體類開始。

課程注冊學生數據模型關系圖

StudentEnrollment實體之間是一對多的關系,CourseEnrollment實體之間也是一個對多的關系。 換而言之,一名學生可以修讀任意數量的課程, 并且某一課程可以被任意數量的學生修讀。

接下來,你將創建與這些實體對應的類。

學生實體

學生實體關系圖

Models文件夾中,創建一個名為Student.cs的類文件并且將模板代碼替換為以下代碼。

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屬性將成為對應于此類的數據庫表中的主鍵。 默認情況下,EF 將會將名為IDclassnameID的屬性解析為主鍵。

Enrollments屬性是導航屬性。 導航屬性中包含與此實體相關的其他實體。 在這個案例下,Student entity中的Enrollments屬性會保留所有與Student實體相關的Enrollment。 換而言之,如果在數據庫中有兩行描述同一個學生的修讀情況 (兩行的 StudentID 值相同,而且 StudentID 作為外鍵和某位學生的主鍵值相同),Student實體的Enrollments導航屬性將包含那兩個Enrollment實體。

如果導航屬性可以具有多個實體 (如多對多或一對多關系),那么導航屬性的類型必須是可以添加、 刪除和更新條目的容器,如ICollection<T>。 你可以指定ICollection<T>或實現該接口類型,如List<T>HashSet<T>。 如果指定ICollection<T>,EF在默認情況下創建HashSet<T>集合。

修讀實體

修讀實體關系圖

Models文件夾中,創建Enrollment.cs并且用以下代碼替換現有代碼:

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

EnrollmentID屬性將被設為主鍵; 此實體使用classnameID模式而不是如Student實體那樣直接使用ID。 通常情況下,你選擇一個主鍵模式,并在你的數據模型自始至終使用這種模式。 在這里,使用了兩種不同的模式只是為了說明你可以使用任一模式來指定主鍵。 在后面的教程,你將了解到使用ID這種模式可以更輕松地在數據模型之間實現繼承。

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

StudentID屬性是一個外鍵,Student是與其且對應的導航屬性。 Enrollment實體與一個Student實體相關聯,因此該屬性只包含單個Student實體 (與前面所看到的Student.Enrollments導航屬性不同后,Student中可以容納多個Enrollment實體)。

CourseID屬性是一個外鍵,Course是與其對應的導航屬性。 Enrollment實體與一個Course實體相關聯。

如果一個屬性名為<導航屬性名><主鍵屬性名>,Entity Framework 就會將這個屬性解析為外鍵屬性(例如,Student實體的主鍵是ID,StudentEnrollment的導航屬性所以Enrollment實體中StudentID會被解析為外鍵)。 此外還可以將需要解析為外鍵的屬性命名為<主鍵屬性名>(例如,CourseID由于Course實體的主鍵所以CourseID也被解析為外鍵)。

課程實體

課程實體關系圖

Models文件夾中,創建Course.cs并且用以下代碼替換現有代碼:

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實體可以與任意數量的Enrollment實體相關。

我們在本系列后面的教程中將會有更多有關DatabaseGenerated特性的例子。 簡單來說,此特性讓您能自行指定主鍵,而不是讓數據庫自動指定主鍵。

創建數據庫上下文

使得給定的數據模型與 Entity Framework 功能相協調的主類是數據庫上下文類。 可以通過繼承 Microsoft.EntityFrameworkCore.DbContext 類的方式創建此類。 在該類中你可以指定數據模型中包含哪些實體。 你還可以定義某些 Entity Framework 行為。 在此項目中將數據庫上下文類命名為SchoolContext。

在項目文件夾中,創建名為的文件夾Data。

Data文件夾創建名為SchoolContext.cs的類文件,并將模板代碼替換為以下代碼:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

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

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

此代碼將為每個實體集創建DbSet屬性。 在 Entity Framework 中,實體集通常與數據表相對應,具體實體與表中的行相對應。

在這里可以省略DbSet<Enrollment>DbSet<Course>語句,實現的功能沒有任何改變。 Entity Framework 會隱式包含這兩個實體因為Student實體引用了Enrollment實體、Enrollment實體引用了Course實體。

當數據庫創建完成后, EF 創建一系列數據表,表名默認和DbSet屬性名相同。 集合屬性的名稱一般使用復數形式,但不同的開發人員的命名習慣可能不一樣,開發人員根據自己的情況確定是否使用復數形式。在最后一個 DbSet 屬性之后添加以下高亮顯示的代碼在定義 DbSet 屬性的代碼之后添加下面高亮代碼,對 DbContext 指定單數的表明來覆蓋默認的表名。

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

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

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

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

使用依賴注入注冊上下文

ASP.NET Core 默認實現依賴注入。 在應用程序啟動過程通過依賴注入注冊相關服務 (例如 EF 數據庫上下文)。 需要這些服務的組件 (如 MVC 控制器) 可以通過向構造函數添加相關參數來獲得對應服務。 在本教程后面你將看到控制器構造函數的代碼,就是通過上述方式獲得上下文實例。

若要將SchoolContext注冊為一種服務,打開Startup.cs,并將添加高亮代碼添加到ConfigureServices方法中。

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

    services.AddMvc();
}

通過調用DbContextOptionsBuilder中的一個方法將數據庫連接字符串在配置文件中的名稱傳遞給上下文對象。 進行本地開發時, ASP.NET Core 配置系統在appsettings.json文件中讀取數據庫連接字符串。

添加using語句引用ContosoUniversity.DataMicrosoft.EntityFrameworkCore命名空間,然后生成項目。

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;

打開appsettings.json文件并添加連接字符串,如下面的示例中所示。

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

SQL Server Express LocalDB

數據庫連接字符串指定使用 SQL Server LocalDB 數據庫。 LocalDB 是 SQL Server Express 數據庫引擎的輕量級版本,用于應用程序開發,不在生產環境中使用。 LocalDB 作為按需啟動并在用戶模式下運行的輕量級數據庫沒有復雜的配置。 默認情況下, LocalDB 在C:/Users/<user>目錄下創建.mdf數據庫文件。

添加代碼以使用測試數據初始化數據庫

Entity Framework 已經為你創建了一個空數據庫。在本部分中,你將編寫一個方法用于向數據庫填充測試數據,該方法會在數據庫創建完成之后執行。

此處將使用EnsureCreated方法來自動創建數據庫。 在后面的教程你將了解如何通過使用 Code First Migration 來更改而不是刪除并重新創建數據庫來處理模型更改。

Data文件夾中,創建名為的新類文件DbInitializer.cs并且將模板代碼替換為以下代碼,使得在需要時能創建數據庫并向其填充測試數據。

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

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

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

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

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

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

這段代碼首先檢查是否有學生數據在數據庫中,如果沒有的話,就可以假定數據庫是新建的,然后使用測試數據進行填充。代碼中使用數組存放測試數據而不是使用List<T>集合是為了優化性能。

Program.cs,修改Main方法,使得在應用程序啟動時能執行以下操作:

  • 從依賴注入容器中獲取數據庫上下文實例。
  • 調用 seed 方法,將上下文傳遞給它。
  • Seed 方法完成此操作時釋放上下文。
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

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

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

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

添加using語句:

using Microsoft.Extensions.DependencyInjection;
using ContosoUniversity.Data;

在舊版教程中,你可能會在Startup.cs中的Configure方法看到類似的代碼。 我們建議你只在為了設置請求管道時使用Configure方法。 將應用程序啟動代碼放入Main方法。

現在首次運行該應用程序,創建數據庫并使用測試數據作為種子數據。 每當你更改你的數據模型,你可以刪除數據庫、 更新你的 Initialize 方法,然后使用上述方式更新新數據庫。 在之后的教程中,你將了解如何在數據模型更改時,只需修改數據庫而無需刪除重建數據庫。

創建控制器和視圖

接下來,將使用 Visual Studio 中的基架引擎添加一個 MVC 控制器和使用 EF 來查詢和保存數據的視圖。

CRUD 操作方法和視圖的自動創建被稱為基架。 基架與代碼生成不同,基架的代碼是一個起點,您可以修改基架以滿足自己需求,而你通常無需修改生成的代碼。 當你需要自定義生成代碼時,你使用一部分類或需求發生變化時重新生成代碼。

  • 右鍵單擊解決方案資源管理器中的Controllers文件夾選擇添加 > 新搭建的基架項目

如果顯示“添加 MVC 依賴項”對話框:

  • 更新 Visual Studio 至最新版本。 當 Visual Studio 版本小于 15.5 時會顯示這個對話框

  • 如果更新失敗,選擇添加, 然后執行添加控制器的步驟。

  • 添加基架的對話框中:

    • 選擇視圖使用 Entity Framework 的 MVC 控制器

    • 單擊添加

  • 添加控制器對話框中:

    • 模型類選擇Student

    • 數據上下文類選擇SchoolContext

    • 使用StudentsController作為默認名字

    • 單擊添加

    基架學生

    當你單擊添加后,Visual Studio 基架引擎創建StudentsController.cs文件和一組對應于控制器的視圖 (.cshtml文件) 。

(如果你之前手動創建數據庫上下文,基架引擎還可以自動創建。 你可以在添加控制器對話框中單擊右側的加號框數據上下文類來指定在一個新上下文類。然后,Visual Studio 將創建你的DbContext,控制器和視圖類。)

你會注意到控制器采用SchoolContext作為構造函數參數。

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

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

ASP.NET 依賴注入機制將會處理傳遞一個SchoolContext實例到控制器。 在前面的教程中已經通過修改Startup.cs文件來配置注入規則。

控制器包含Index操作方法用于顯示數據庫中的所有學生。 該方法從學生實體集中獲取學生列表,學生實體集則是通過讀取數據庫上下文實例中的Students屬性獲得:

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

你將在本教程后面了解此代碼中的異步編程元素。

Views/Students/Index.cshtml視圖使用table標簽顯示此列表:

@model IEnumerable<ContosoUniversity.Models.Student>

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

<h2>Index</h2>

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

按 CTRL + F5 來運行該項目或從菜單選擇調試 > 開始執行(不調試)。

單擊學生選項卡以查看DbInitializer.Initialize插入的測試的數據。 你將看到Student選項卡鏈接在頁的頂部或在單擊右上角后的導航圖標中,具體顯示在哪里取決于瀏覽器窗口寬度。

窄的 Contoso 大學主頁
學生索引頁

查看數據庫

當你啟動了應用程序,DbInitializer.Initialize方法調用EnsureCreated。 EF 沒有檢測到相關數據庫然后自己創建了一個,接著Initialize方法的其余代碼向數據庫中填充數據。 你可以使用 Visual Studio 中的SQL Server 對象資源管理器(SSOX) 查看數據庫。

關閉瀏覽器。

如果 SSOX 窗口尚未打開,請從Visual Studio 中的視圖菜單中選擇。

在 SSOX 中,單擊(localdb) \MSSQLLocalDB > 數據庫,然后單擊和appsettings.json文件中的連接字符串對應的數據庫。

展開節點以查看你的數據庫中的表。

在 SSOX 中的表

右鍵單擊Student表,然后單擊查看數據若要查看已創建的列和已插入到表的行。

在 SSOX 中學生表

.Mdf.ldf數據庫文件位于*C:\Users*文件夾。

因為調用EnsureCreated的初始化方法在啟動應用程序時才運行,所以在這之前你可以更改Student類、 刪除數據庫、 再運行一次應用程序,這時候數據庫將自動重新創建,以匹配所做的更改。 例如,如果向Student類添加EmailAddress屬性,重新的創建表中會有EmailAddress列。

約定

由于 Entity Framwork 有一定的約束條件,你只需要按規則編寫很少的代碼就能夠創建一個完整的數據庫,

  • DbSet類型的屬性用作表名。 實體未被DbSet屬性引用,實體類名稱用作表名稱。

  • 實體屬性名稱用于列名稱。

  • ID 或 classnameID 命名的實體屬性被識別為主鍵屬性。

  • 如果屬性名為 <導航屬性名> <主鍵名>將被解釋為外鍵屬性 (例如,StudentID對應Student導航屬性,Student實體的主鍵是ID,所以StudentID被解釋為外鍵屬性). 此外也可以將外鍵屬性命名為 <主鍵屬性名> (例如,EnrollmentID,由于Enrollment實體的主鍵是EnrollmentID,因此被解釋為外鍵)。

約定行為可以被重寫。 例如,在本教程前半部分的顯式指定表名稱, 在本系列后面教程的設置列名稱和將任何屬性設置為主鍵或外鍵。

異步代碼

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

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

異步代碼在運行時,會引入的少量開銷,在低流量時對性能的影響可以忽略不計,但在針對高流量情況下潛在的性能提升是可觀的。

在下面的代碼中,async關鍵字,Task<T>返回值,await關鍵字,和ToListAsync方法使代碼異步執行。

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • async關鍵字用于告知編譯器該方法主體將生成回調并自動創建Task<IActionResult>返回對象。

  • 返回類型Task<IActionResult>表示正在進行的工作返回的結果為IActionResult類型。

  • await關鍵字會使得編譯器將方法拆分為兩個部分。 第一部分是以異步方式結束已啟動的操作。 第二部分是當操作完成時注入調用回調方法的地方。

  • ToListAsync是由ToList方法的的異步擴展版本。

你使用 Entity Framework 編寫異步代碼時的一些注意事項:

  • 只有導致查詢或發送數據庫命令的語句才能以異步方式執行。 包括 ToListAsync, SingleOrDefaultAsync,和SaveChangesAsync。 不包括,操作IQueryable的語句,如var students = context.Students.Where(s => s.LastName == "Davolio")

  • EF 上下文是線程不安全的: 請勿嘗試并行執行多個操作。 當調用異步 EF 方法時,始終使用await關鍵字。

  • 如果你想要利用異步代碼的性能優勢,請確保你所使用的任何庫和包在它們調用導致 Entity Framework 數據庫查詢方法時也使用異步。
    有關在 .NET 異步編程的詳細信息,請參閱異步概述。

總結

你現在已創建了一個使用 Entity Framework Core 和 SQL Server Express LocalDB 來存儲和顯示數據的簡單應用程序。 在下一個教程中,你將學習如何執行基本的 CRUD (創建、 讀取、 更新、 刪除) 操作。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容