練習 - 設定移轉

已完成

在此單元中,您將建立 C# 實體類別,這些實體類別會對應到本機 SQLite 資料庫中的資料表。 EF Core 移轉功能會根據這些實體產生資料表。

移轉會提供一種累加式更新資料庫結構描述的方式。

取得專案檔案

若要開始使用,請取得專案檔案。 您有一些取得專案檔案方式的選項:

  • 使用 GitHub Codespaces
  • 複製 GitHub 存放庫

如果已安裝相容的容器執行階段,您也可以使用 Dev Containers 擴充功能,在預先安裝工具的容器中開啟存放庫。

使用 GitHub Codespaces

Codespace 是雲端主控的 IDE。 如果使用 GitHub Codespaces,請移至瀏覽器中的存放庫。 選取 [程式碼],並在 main 分支建立新的 Codespace。

複製 GitHub 存放庫

如果未使用 GitHub Codespaces,您可以複製專案 GitHub 存放庫,並在 Visual Studio Code 中以資料夾開啟檔案。

  1. 開啟命令終端機,並使用命令提示字元從 GitHub 複製專案:

    git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
    
  2. 移至 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.csApiController 的值,可公開 HTTP POST、GET、PUT 和 DELETE 動詞命令的端點。 這些動詞命令會呼叫 PizzaService 上相對應的 CRUD 方法。 PizzaService 會插入至 PizzaController 建構函式。
  • Models 資料夾包含 PizzaServicePizzaController 所使用的模型。
  • 實體模型 Pizza.csTopping.csSauce.cs 具有下列關聯性:
    • 披薩可有一或多種配料。
    • 配料可在一或多個披薩上使用。
    • 披薩可有一個醬料,但醬料可在多個披薩上使用。

建置應用程式

若要在 Visual Studio Code 中建置應用程式:

  1. 在 [檔案總管] 窗格上,以滑鼠右鍵按一下 [ContosoPizza] 目錄並選取 [在整合式終端機開啟]。

    隨即開啟範圍設定為 ContosoPizza 目錄的終端機窗格。

  2. 使用下列命令建置應用程式:

    dotnet build
    

    程式碼建置不應產生警告或錯誤。

新增 NuGet 套件和 EF Core 工具

您在此課程模組使用的資料庫引擎為 SQLite。 SQLite 是輕量型、以檔案為基礎的資料庫引擎。 這是實際執行和測試的絕佳選擇,同時也是小規模實際執行部署理想選擇。

注意

如先前所述,EF Core 中的資料庫提供者為可外掛式。 SQLite 是此課程模組的絕佳選擇,原因在於該系統為輕量型且可跨平台。 您可以使用相同程式碼來使用不同資料庫引擎,例如 SQL Server 和 PostgreSQL。 您甚至可以在相同應用程式中使用多個資料庫引擎。

開始之前,您必須新增必要套件:

  1. 在終端機窗格上執行下列命令:

    dotnet add package Microsoft.EntityFrameworkCore.Sqlite
    

    此命令會新增含有 EF Core SQLite 資料庫提供者及其相依性的 NuGet 套件,包括常用的 EF Core 服務。

  2. 執行此命令:

    dotnet add package Microsoft.EntityFrameworkCore.Design
    

    此命令會新增 EF Core 工具所需的套件。

  3. 若要完成,請執行此命令:

    dotnet tool install --global dotnet-ef
    

    此命令會安裝 dotnet ef,您將使用此工具來建立移轉和 Scaffolding。

    提示

    如果已安裝 dotnet ef,您可以執行 dotnet tool update --global dotnet-ef 進行更新。

建構模型和 DbContext

您現在將新增並設定 DbContext 實作。 DbContext 是閘道,您可透過該閘道與資料庫互動。

  1. 以滑鼠右鍵按一下 ContosoPizza 目錄並新增名為 Data 的資料夾。

  2. 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 將公開 PizzasToppingsSauces 屬性。 您對這些屬性公開的集合所作變更,將會傳播至資料庫。
  3. 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 等服務。

  4. 還有在 Program.cs 中,將 // Additional using declarations 取代為下列程式碼。

    using ContosoPizza.Data;
    

    此程式碼可解析上一步的相依性。

  5. 儲存您的所有變更。 GitHub Codespaces 會自動儲存您的變更。

  6. 執行 dotnet build,在終端機中建置應用程式。 建置應會成功,且沒有任何警告或錯誤。

建立並執行移轉

接下來,建立移轉以用於建立初始資料庫。

  1. 執行下列命令來產生用於建立資料庫資料表的移轉:

    dotnet ef migrations add InitialCreate --context PizzaContext
    

    在上述命令中︰

    • 移轉獲指定 InitialCreate 名稱。
    • --context 選項會指定 ContosoPizza 專案中的類別名稱,其衍生自 DbContext

    ContosoPizza 專案根目錄中會出現新的 Migrations 目錄。 此目錄包含 <timestamp>_InitialCreate.cs 檔案,描述要轉譯成資料定義語言 (DDL) 變更指令碼的資料庫變更。

  2. 執行下列命令,以套用 InitialCreate 移轉:

    dotnet ef database update --context PizzaContext
    

    此命令會套用移轉。 ContosoPizza.db 不存在,因此會在專案目錄中建立移轉。

    提示

    所有平台都支援 dotnet ef 工具。 在 Windows 版 Visual Studio 中,您可以在整合的 [套件管理員主控台] 視窗中執行 Add-MigrationUpdate-Database PowerShell Cmdlet。

檢查資料庫

EF Core 已為您的應用程式建立資料庫。 接下來,使用 SQLite 擴充功能查看資料庫內部。

  1. 在 [檔案總管] 窗格上,以滑鼠右鍵按一下 [ContosoPizza.db] 檔案並選擇 [開啟資料庫]。

    螢幕擷取畫面顯示 Visual Studio Code [檔案總管] 窗格中的 [開始資料庫] 功能表選項。

    [SQLite Explorer] 資料夾會顯示在 [檔案總管] 窗格上。

    螢幕擷取畫面顯示 [檔案總管] 窗格上的 [SQLite Explorer] 資料夾。

  2. 選擇 [SQLite Explorer] 以展開節點和其所有子節點。 以滑鼠右鍵按一下 [ContosoPizza.db] 並選擇 [顯示資料表 'sqlite_master'],檢視移轉建立的完整資料庫結構描述和限制式。

    螢幕擷取畫面顯示 [檔案總管] 窗格上的已展開 [SQLite Explorer] 資料夾。

    • 已建立對應到每個實體的資料表。
    • 資料表名稱取自 PizzaContextDbSet 屬性的名稱。
    • 名為 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 慣例可以透過推斷開發人員的意圖來縮短開發時間。 例如,名為 Id<entity name>Id 的屬性會推斷為所產生資料表主索引鍵。 若您選擇不採用命名慣例,則必須使用 [Key] 屬性標註該屬性,或在 DbContextOnModelCreating 方法中設定為索引鍵。

變更模型並更新資料庫結構

Contoso Pizza 經理提供一些新需求,因此您必須變更您的實體模型。 在以下步驟中,您使用對應屬性 (有時稱為「資料註解」) 修改模型。

  1. Models\Pizza.cs中,進行下列變更:

    1. System.ComponentModel.DataAnnotations 新增 using 指示詞。
    2. Name 屬性之前新增 [Required] 屬性,將屬性標示為必要。
    3. Name 屬性之前新增 [MaxLength(100)] 屬性,指定字串長度上限為 100。
    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; }
    }
    
  2. Models\Sauce.cs 中,進行下列變更:

    1. System.ComponentModel.DataAnnotations 新增 using 指示詞。
    2. Name 屬性之前新增 [Required] 屬性,將屬性標示為必要。
    3. Name 屬性之前新增 [MaxLength(100)] 屬性,指定字串長度上限為 100。
    4. 新增名為 IsVeganbool 屬性。
    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; }
    }
    
  3. Models\Topping.cs 中,進行下列變更:

    1. System.ComponentModel.DataAnnotationsSystem.Text.Json.Serialization 新增 using 指示詞。
    2. Name 屬性之前新增 [Required] 屬性,將屬性標示為必要。
    3. Name 屬性之前新增 [MaxLength(100)] 屬性,指定字串長度上限為 100。
    4. 緊接在 Name 屬性之後,新增名為 Caloriesdecimal 屬性。
    5. 新增類型為 ICollection<Pizza>?Pizzas 屬性,以建立 Pizza-Topping 多對多關係。
    6. [JsonIgnore] 屬性新增至 Pizzas 屬性。

    重要

    這些步驟為了防止 Topping 實體在 Web API 程式碼序列化 JSON 的回應時納入 Pizzas 屬性。 如果未變更,配料的序列化集合會包含使用該配料的每個披薩集合。 在集合中的每個披薩都會包含一個配料集合,每個配料集合都會再次包含披薩集合。 這種類型的無限迴圈稱為迴圈參考,無法序列化。

    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; }
    }
    
  4. 儲存您所有的變更並執行 dotnet build

  5. 執行下列命令來產生用於建立資料庫資料表的移轉:

    dotnet ef migrations add ModelRevisions --context PizzaContext
    

    建立名為 ModelRevisions 的移轉。

    注意

    隨即顯示此訊息:「已建構作業,可能會導致資料遺失。請檢閱移轉的精確度。」。 顯示訊息的原因是您已將關聯性從 Pizza 變更至 Topping (一對多變更為多對多),這需要捨棄現有的外部索引鍵資料行。 由於您尚未在資料庫中有任何資料,因此此變更不會發生問題。 然而,一般來說,建議您在顯示此警告時檢查產生的移轉,以確保移轉不會刪除或截斷任何資料。

  6. 執行下列命令,以套用 ModelRevisions 移轉:

    dotnet ef database update --context PizzaContext
    
  7. 在 [SQLite Explorer] 資料夾的標題列上,選取 [重新整理資料庫] 按鈕。

    螢幕擷取畫面顯示 [SQLite Explorer] 標題列上的 [重新整理資料庫] 按鈕。

  8. 在 [SQLite Explorer] 資料夾中,以滑鼠右鍵按一下 [ContosoPizza.db]。 選取 [顯示資料表 'sqlite_master'],檢視完整資料庫結構和限制。

    重要

    SQLite 擴充功能會重複使用開啟的 [SQLite] 索引標籤。

    • 建立了 PizzaTopping 聯結資料表,以代表披薩和配料之間的多對多關係。
    • 新欄位已新增至 ToppingsSauces
      • Calories 定義為 text 資料行,因為 SQLite 沒有相符的 decimal 類型。
      • 同樣地,IsVegan 定義為 integer 資料行。 SQLite 不會定義 bool 類型。
      • 在這兩種情況下,EF Core 都會管理翻譯。
    • 每個資料表中的 Name 資料行已標示 not null,但 SQLite 沒有 MaxLength 條件約束。

    提示

    EP Core 資料庫提供者會將模型結構描述對應到特定資料庫的功能。 雖然 SQLite 不會針對 MaxLength 實作對應的限制式,但其他資料庫 (例如 SQL Server 和 PostgreSQL) 則會實作。

  9. 在 [SQLite Explorer] 資料夾中,以滑鼠右鍵按一下 _EFMigrationsHistory 資料表並選取 [顯示資料表]。 資料表包含套用至資料庫的所有移轉清單。 由於您已執行兩個移轉,因此有兩個實體:一個實體用於 InitialCreate 移轉,另一個實體用於 ModelRevisions

注意

本練習使用對應屬性 (資料註解) 以將模型對應到資料庫。 作為對應屬性的替代方式,您可使用 ModelBuilder Fluent API 來設定模型。 這兩種方法都有效,但某些開發人員的偏好使用方法可能不同。

您已使用移轉來定義和更新資料庫結構。 在下一個單元中,您將完成 PizzaService 中操縱資料的方法。

檢定您的知識

1.

在實體類別中,主索引鍵的屬性命名慣例為何?