練習 - 設定移轉
在此單元中,您將建立 C# 實體類別,這些實體類別會對應到本機 SQLite 資料庫中的資料表。 EF Core 移轉功能會根據這些實體產生資料表。
移轉會提供一種累加式更新資料庫結構描述的方式。
取得專案檔案
若要開始使用,請取得專案檔案。 您有一些取得專案檔案方式的選項:
- 使用 GitHub Codespaces
- 複製 GitHub 存放庫
如果已安裝相容的容器執行階段,您也可以使用 Dev Containers 擴充功能,在預先安裝工具的容器中開啟存放庫。
使用 GitHub Codespaces
Codespace 是雲端主控的 IDE。 如果使用 GitHub Codespaces,請移至瀏覽器中的存放庫。 選取 [程式碼],並在 main
分支建立新的 Codespace。
複製 GitHub 存放庫
如果未使用 GitHub Codespaces,您可以複製專案 GitHub 存放庫,然後在 Visual Studio Code 中以資料夾開啟檔案。
開啟命令終端機,並使用命令提示字元從 GitHub 複製專案:
git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
移至 mslearn-persist-data-ef-core 資料夾,並在 Visual Studio Code 中開啟專案:
cd mslearn-persist-data-ef-core code .
檢閱程式碼
現在您已經有了可以使用的專案檔,讓我們看看專案中有什麼並檢閱程式碼。
- ASP.NET Core Web API 專案位於 ContosoPizza 目錄中。 我們在本課程模組中參考的檔案路徑相對於 ContosoPizza 目錄。
- Services/PizzaService.cs 是服務類別,可定義建立、讀取、更新及刪除 (CRUD) 方法。 所有方法目前都擲出
System.NotImplementedException
。 - 在 Program.cs 中,
PizzaService
已向 ASP.NET Core 相依性插入系統註冊。 - Controllers/PizzaController.cs 是
ApiController
的值,可公開 HTTP POST、GET、PUT 和 DELETE 動詞命令的端點。 這些動詞命令會呼叫PizzaService
上相對應的 CRUD 方法。PizzaService
會插入至PizzaController
建構函式。 - Models 資料夾含有
PizzaService
和PizzaController
所使用的模型。 - 實體模型 Pizza.cs、Topping.cs 和 Sauce.cs 具有下列關聯性:
- 披薩可有一或多種配料。
- 配料可在一或多個披薩上使用。
- 披薩可有一個醬料,但醬料可在多個披薩上使用。
建置應用程式
若要在 Visual Studio Code 中建置應用程式:
在 [檔案總管] 窗格上,以滑鼠右鍵按一下 [ContosoPizza] 目錄並選取 [在整合式終端機開啟]。
隨即開啟範圍設定為 ContosoPizza 目錄的終端機窗格。
使用下列命令建置應用程式:
dotnet build
程式碼建置不應產生警告或錯誤。
新增 NuGet 套件和 EF Core 工具
您在此課程模組使用的資料庫引擎為 SQLite。 SQLite 是輕量型、以檔案為基礎的資料庫引擎。 這是實際執行和測試的絕佳選擇,同時也是小規模實際執行部署理想選擇。
注意
如先前所述,EF Core 中的資料庫提供者為可外掛式。 SQLite 是此課程模組的絕佳選擇,原因在於該系統為輕量型且可跨平台。 您可以使用相同程式碼來使用不同資料庫引擎,例如 SQL Server 和 PostgreSQL。 您甚至可以在相同應用程式中使用多個資料庫引擎。
開始之前,您必須新增必要套件:
在終端機窗格中執行下列命令:
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
此命令會新增含有 EF Core SQLite 資料庫提供者及其相依性的 NuGet 套件,包括常用的 EF Core 服務。
接下來,執行此命令:
dotnet add package Microsoft.EntityFrameworkCore.Design
此命令會新增 EF Core 工具所需的套件。
若要完成,請執行此命令:
dotnet tool install --global dotnet-ef
此命令會安裝
dotnet ef
,您可以使用此工具來建立移轉和 Scaffolding。提示
如果已安裝
dotnet ef
,您可以執行dotnet tool update --global dotnet-ef
進行更新。
建構模型和 DbContext
您現在將新增並設定 DbContext
實作。 DbContext
是閘道,您可透過該閘道與資料庫互動。
以滑鼠右鍵按一下 ContosoPizza 目錄並新增名為 Data 的資料夾。
在 Data 資料夾中,建立名為 PizzaContext.cs 的檔案。 將下列程式碼新增到空白檔案:
using Microsoft.EntityFrameworkCore; using ContosoPizza.Models; namespace ContosoPizza.Data; public class PizzaContext : DbContext { public PizzaContext (DbContextOptions<PizzaContext> options) : base(options) { } public DbSet<Pizza> Pizzas => Set<Pizza>(); public DbSet<Topping> Toppings => Set<Topping>(); public DbSet<Sauce> Sauces => Set<Sauce>(); }
在上述程式碼中:
- 建構函式接受類型
DbContextOptions<PizzaContext>
的參數。 建構函式可讓外部程式碼傳入設定,使測試和實際執行程式碼之間可以共用相同的DbContext
,且甚至可以與不同提供者搭配使用。 DbSet<T>
屬性會對應到要在資料庫中建立的資料表。- 資料表名稱會符合
PizzaContext
類別中的DbSet<T>
屬性名稱。 您可以視需要覆寫此行為。 - 具現化時,
PizzaContext
會公開Pizzas
、Toppings
和Sauces
屬性。 您對這些屬性公開的集合所做的變更會傳播到資料庫。
- 建構函式接受類型
在 Program.cs 中,將
// Add the PizzaContext
取代為下列程式碼:builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
上述 程式碼:
- 向 ASP.NET Core 相依性插入系統註冊
PizzaContext
。 - 指定
PizzaContext
使用 SQLite 資料庫提供者。 - 定義指向本機檔案 ContosoPizza.db的 SQLite 連接字串。
注意
SQLite 使用本機資料庫檔案,因此可以硬式編碼連接字串。 針對 PostgreSQL 和 SQL Server 等網路資料庫,您應該一律採用安全方式儲存連接字串。 針對本機開發,請使用秘密管理員。 針對實際執行部署,請考慮使用 Azure Key Vault 等服務。
- 向 ASP.NET Core 相依性插入系統註冊
還有在 Program.cs 中,將
// Additional using declarations
取代為下列程式碼。using ContosoPizza.Data;
此程式碼可解析上一步的相依性。
儲存您的所有變更。 GitHub Codespaces 會自動儲存您的變更。
執行
dotnet build
,在終端機中建置應用程式。 建置應會成功,且沒有任何警告或錯誤。
建立並執行移轉
接下來,建立移轉以用於建立初始資料庫。
在範圍設為 ContosoPizza 專案資料夾的終端機中,執行以下命令來產生用於建立資料庫資料表的移轉:
dotnet ef migrations add InitialCreate --context PizzaContext
在上述命令中:
- 移轉名為:InitialCreate。
--context
選項會指定 ContosoPizza 專案中的類別名稱,其衍生自DbContext
。
ContosoPizza 專案根目錄中會出現新的 Migrations 目錄。 此目錄包含 <timestamp>_InitialCreate.cs 檔案,描述要轉譯成資料定義語言 (DDL) 變更指令碼的資料庫變更。
執行下列命令,以套用 InitialCreate 移轉:
dotnet ef database update --context PizzaContext
此命令會套用移轉。 ContosoPizza.db 不存在,因此此命令會在專案目錄中建立移轉。
提示
所有平台都支援
dotnet ef
工具。 在 Windows 版 Visual Studio 中,您可以在整合的 [套件管理員主控台] 視窗中執行Add-Migration
與Update-Database
PowerShell Cmdlet。
檢查資料庫
EF Core 已為您的應用程式建立資料庫。 接下來,讓我們使用 SQLite 延伸模組來查看資料庫內部。
在 [檔案總管] 窗格上,以滑鼠右鍵按一下 [ContosoPizza.db] 檔案並選擇 [開啟資料庫]。
[SQLite Explorer] 資料夾會顯示在 [檔案總管] 窗格中。
選擇 [SQLite Explorer] 以展開節點和其所有子節點。 以滑鼠右鍵按一下 [ContosoPizza.db] 並選擇 [顯示資料表 'sqlite_master'],檢視移轉建立的完整資料庫結構描述和限制式。
- 已建立對應到每個實體的資料表。
- 資料表名稱取自
PizzaContext
上DbSet
屬性的名稱。 - 名為
Id
的屬性推斷為自動遞增主索引鍵欄位。 - EF Core 主索引鍵和外部索引鍵限制式的命名慣例分別是
PK_<primary key property>
和FK_<dependent entity>_<principal entity>_<foreign key property>
。<dependent entity>
和<principal entity>
預留位置對應到實體類別名稱。
注意
如同 ASP.NET Core MVC,EF Core 會使用「慣例優先設定」方法。 EF Core 慣例可以透過推斷開發人員的意圖來縮短開發時間。 例如,EF Core 會推斷名為
Id
或<entity name>Id
的屬性將成為產生的資料表的主索引鍵。 如果您選擇不採用命名慣例,則必須使用[Key]
屬性來標註屬性,或在DbContext
的OnModelCreating
方法中將它設定為索引鍵。
變更模型並更新資料庫結構
Contoso Pizza 經理提供一些新需求,因此您必須變更您的實體模型。 在以下步驟中,您使用對應屬性 (有時稱為「資料註解」) 修改模型。
在 Models\Pizza.cs中,進行下列變更:
- 為
System.ComponentModel.DataAnnotations
新增using
指示詞。 - 在
Name
屬性之前新增[Required]
屬性,將屬性標示為必要。 - 在
Name
屬性之前新增[MaxLength(100)]
屬性,指定字串長度上限為 100。
您更新的 Pizza.cs 檔案看起來應該類似下列程式碼:
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Pizza { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public Sauce? Sauce { get; set; } public ICollection<Topping>? Toppings { get; set; } }
- 為
在 Models\Sauce.cs 中,進行下列變更:
- 為
System.ComponentModel.DataAnnotations
新增using
指示詞。 - 在
Name
屬性之前新增[Required]
屬性,將屬性標示為必要。 - 在
Name
屬性之前新增[MaxLength(100)]
屬性,指定字串長度上限為 100。 - 新增名為
IsVegan
的bool
屬性。
您更新的 Sauce.cs 檔案看起來應該類似下列程式碼:
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Sauce { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public bool IsVegan { get; set; } }
- 為
在 Models\Topping.cs 中,進行下列變更:
為
System.ComponentModel.DataAnnotations
和System.Text.Json.Serialization
新增using
指示詞。在
Name
屬性之前新增[Required]
屬性,將屬性標示為必要。在
Name
屬性之前新增[MaxLength(100)]
屬性,指定字串長度上限為 100。緊接在
Name
屬性之後,新增名為Calories
的decimal
屬性。新增類型
ICollection<Pizza>?
的Pizzas
屬性。 這項變更會讓Pizza
-Topping
成為多對多關聯性。將
[JsonIgnore]
屬性新增至Pizzas
屬性。重要
當 Web API 程式碼將回應序列化為 JSON 時,這個屬性可防止
Topping
實體包含Pizzas
屬性。 如果未變更,配料的序列化集合會包含使用該配料的每個披薩集合。 在該集合中的每個披薩都會包含一個配料集合,每個配料集合都會再次包含披薩集合。 這種類型的無限迴圈稱為迴圈參考,無法序列化。
您更新的 Topping.cs 檔案看起來應該類似下列程式碼:
using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace ContosoPizza.Models; public class Topping { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public decimal Calories { get; set; } [JsonIgnore] public ICollection<Pizza>? Pizzas { get; set; } }
儲存您所有的變更並執行
dotnet build
。執行下列命令來產生用於建立資料庫資料表的移轉:
dotnet ef migrations add ModelRevisions --context PizzaContext
此命令會建立名為 ModelRevisions 的移轉。
注意
您會看到此訊息:已建構一個作業,可能會導致資料遺失。請檢閱移轉以確保正確性。 出現此訊息的原因是您將關聯性從
Pizza
變更為Topping
,從一對多變更為多對多,這需要卸除現有的外鍵資料行。 由於您尚未在資料庫中有任何資料,因此此變更不會發生問題。 不過,一般而言,最好在出現此警告時檢查所產生的移轉,以確保移轉不會刪除或截斷任何資料。執行下列命令,以套用 ModelRevisions 移轉:
dotnet ef database update --context PizzaContext
在 [SQLite Explorer] 資料夾的標題列上,選取 [重新整理資料庫] 按鈕。
在 [SQLite Explorer] 資料夾中,以滑鼠右鍵按一下 [ContosoPizza.db]。 選取 [顯示資料表 'sqlite_master'],檢視完整資料庫結構和限制。
重要
SQLite 擴充功能會重複使用開啟的 [SQLite] 索引標籤。
- 建立了
PizzaTopping
聯結資料表,以代表披薩和配料之間的多對多關係。 - 新的欄位已新增至
Toppings
和Sauces
。Calories
定義為text
資料行,因為 SQLite 沒有相符的decimal
類型。- 同樣地,
IsVegan
定義為integer
資料行。 SQLite 不會定義bool
類型。 - 在這兩種情況下,EF Core 都會管理翻譯。
- 每個資料表中的
Name
資料行已標示not null
,但 SQLite 沒有MaxLength
條件約束。
提示
EP Core 資料庫提供者會將模型結構描述對應到特定資料庫的功能。 雖然 SQLite 不會針對
MaxLength
實作對應的限制式,但其他資料庫 (例如 SQL Server 和 PostgreSQL) 則會實作。- 建立了
在 [SQLite Explorer] 資料夾中,以滑鼠右鍵按一下
_EFMigrationsHistory
資料表並選取 [顯示資料表]。 資料表包含套用至資料庫的所有移轉清單。 由於您已執行兩個移轉,因此有兩個實體:一個實體用於 InitialCreate 移轉,而另一個實體用於 ModelRevisions。
注意
本練習使用對應屬性 (資料註解) 以將模型對應到資料庫。 作為對應屬性的替代方式,您可使用 ModelBuilder Fluent API 來設定模型。 這兩種方法都有效,但某些開發人員的偏好使用方法可能不同。
您已使用移轉來定義和更新資料庫結構。 在下一個單元中,您將完成 PizzaService
中操縱資料的方法。