此處提供了本教程的 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,請參閱本系列教程的上一個版本。
- 本教程的 ASP.NET Core 1.1 版本,請參閱本教程中 VS 2017 Update 2 版本的 PDF 文檔。
- 有關本教程的 Visual Studio 2015 版本,請參閱 ASP.NET Core 文檔 VS 2015 版本的 PDF 文檔。
準備
安裝以下組件:
- .NET Core 2.0.0 SDK 或更高版本。
- 已安裝 ASP.NET 和 Web 開發工作負載的 Visual Studio 2017 15.3 版或更高版本。
疑難解答
如果你遇到無法解決的問題,可以通過比較已完成的項目查找解決方案。常見錯誤以及對應的解決方案,請參閱最新教程中的故障排除。 如果沒有找到遇到的問題的解決方案你可以將問題發布到StackOverflow.com 的 ASP.NET Core或EF 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.0和Web 應用程序 (模型-視圖-控制器)模板。
注意:本教程需要安裝 ASP.NET Core 2.0 和 EF Core 2.0 或更高版本-請確保ASP.NET Core 1.1未選中。
請確保身份驗證設置為不進行身份驗證。
-
單擊“確定”
“新建 ASP.NET 項目”對話框
設置站點樣式
通過幾個簡單的更改設置站點菜單、 布局和主頁。
打開Views/Shared/_Layout.cshtml并進行以下更改:
將文件中的”ContosoUniversity”更改為”Contoso University”。 需要更改三個地方。
添加菜單項Students,Courses,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>© 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 »</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 »</a></p>
</div>
</div>
按 CTRL + F5 來運行該項目或從菜單選擇調試 > 開始執行不調試。 你會看到首頁和將通過這個教程創建的頁對應的選項卡。
Entity Framework Core NuGet 包
若要為項目添加 EF Core 支持,需要安裝相應的數據庫驅動包。 本教程使用 SQL Server,相關驅動包Microsoft.EntityFrameworkCore.SqlServer。 該包包含在Microsoft.AspNetCore.All 包中,因此不需要手動安裝。
此包和其依賴項 (Microsoft.EntityFrameworkCore
和Microsoft.EntityFrameworkCore.Relational
) 一起提供 EF 的運行時支持。 你將在之后的遷移教程中學習添加工具包。
有關其他可用于 EF Core 的數據庫驅動的信息,請參閱數據庫驅動。
創建數據模型
接下來你將創建 Contoso 大學應用程序的實體類。 你將從以下三個實體類開始。
Student
和Enrollment
實體之間是一對多的關系,Course
和Enrollment
實體之間也是一個對多的關系。 換而言之,一名學生可以修讀任意數量的課程, 并且某一課程可以被任意數量的學生修讀。
接下來,你將創建與這些實體對應的類。
學生實體
在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 將會將名為ID
或classnameID
的屬性解析為主鍵。
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
,Student
是Enrollment
的導航屬性所以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.Data
和Microsoft.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
選項卡鏈接在頁的頂部或在單擊右上角后的導航圖標中,具體顯示在哪里取決于瀏覽器窗口寬度。
查看數據庫
當你啟動了應用程序,DbInitializer.Initialize
方法調用EnsureCreated
。 EF 沒有檢測到相關數據庫然后自己創建了一個,接著Initialize
方法的其余代碼向數據庫中填充數據。 你可以使用 Visual Studio 中的SQL Server 對象資源管理器(SSOX) 查看數據庫。
關閉瀏覽器。
如果 SSOX 窗口尚未打開,請從Visual Studio 中的視圖菜單中選擇。
在 SSOX 中,單擊(localdb) \MSSQLLocalDB > 數據庫,然后單擊和appsettings.json文件中的連接字符串對應的數據庫。
展開表節點以查看你的數據庫中的表。
右鍵單擊Student表,然后單擊查看數據若要查看已創建的列和已插入到表的行。
.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 (創建、 讀取、 更新、 刪除) 操作。