這支影片及逐步導覽介紹針對新資料庫的 Code First 開發。 這種情況包括以一個不存在但 Code First 會建立的資料庫為目標,或一個 Code First 會新增資料表的空資料庫。 Code First 允許你用 C# 或 VB.Net 類別來定義你的模型。 您可以選擇性地透過在類別與屬性上使用屬性,或者使用 Fluent API 來進行額外的配置。
觀看影片
本影片介紹針對新資料庫的 Code First 開發。 這種情況包括鎖定一個目前不存在但將由 Code First 建立的資料庫,或是一個空資料庫,Code First 將會新增新的資料表。 Code First 允許你用 C# 或 VB.Net 類別來定義你的模型。 您可以選擇性地使用類別及屬性的屬性或者 Fluent API 來進行額外的設定。
主持人: 羅恩·米勒
先決條件
你需要至少安裝 Visual Studio 2010 或 Visual Studio 2012 才能完成這個攻略。
如果你用的是 Visual Studio 2010,也需要安裝 NuGet 。
1. 建立應用程式
為了簡化流程,我們將建立一個基本的主控台應用程式,使用 Code First 來執行資料存取。
- 開啟 Visual Studio
- 檔案 -> 新檔 -> 專案...
- 從左側選單和主控台應用程式中選擇 Windows
- 輸入 CodeFirstNewDatabaseSample 作為名稱
- 選擇 確定
2. 建立模型
讓我們用類別定義一個非常簡單的模型。 我們只是在Program.cs檔案中定義它們,但在實際應用中,你會把類別拆分成獨立檔案,甚至可能獨立成專案。
在Program.cs的程式類別定義下方,新增以下兩個類別。
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
你會注意到我們讓兩個導航屬性(Blog.Posts 和 Post.Blog)變成虛擬的。 這使 Entity Framework 的 Lazy Loading 功能得以啟用。 懶惰載入是指當你嘗試存取這些屬性時,這些屬性的內容會自動從資料庫載入。
3. 建立情境
現在是時候定義一個衍生上下文,代表與資料庫的會話,讓我們能夠查詢並儲存資料。 我們定義了一個上下文,其源自 System.Data.Entity.DbContext,並為模型中的每個類別提供一個類型化的 DbSet<TEntity> 。
我們現在開始使用 Entity Framework 的型別,因此需要新增 EntityFramework NuGet 套件。
- 專案 –> 管理 NuGet 套件... 注意:如果你沒有 「管理 NuGet 套件」這個選項, 請安裝 最新版本的 NuGet
- 選擇 線上 標籤
- 選擇 EntityFramework 套件
- 按一下 [安裝]
在 Program.cs 頂部新增 System.Data.Entity 的 using 語句。
using System.Data.Entity;
在 Program.cs 的 Post 類別下方,補充以下衍生的上下文。
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
以下是Program.cs現在應該包含的完整清單。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
namespace CodeFirstNewDatabaseSample
{
class Program
{
static void Main(string[] args)
{
}
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
}
這就是我們開始儲存和檢索資料所需的全部程式碼。 顯然幕後有不少事情在進行,我們稍後會詳細說明,但先讓我們看看實際運作。
4. 讀取與寫入資料
請在 Program.cs 中如下面所示實現 Main 方法。 這段程式碼會建立一個新的上下文實例,然後用它插入新的部落格。 接著它會用 LINQ 查詢,從資料庫中依標題字母順序取得所有部落格。
class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
Console.Write("Enter a name for a new Blog: ");
var name = Console.ReadLine();
var blog = new Blog { Name = name };
db.Blogs.Add(blog);
db.SaveChanges();
// Display all Blogs from the database
var query = from b in db.Blogs
orderby b.Name
select b;
Console.WriteLine("All blogs in the database:");
foreach (var item in query)
{
Console.WriteLine(item.Name);
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
}
你現在可以執行應用程式並測試它。
Enter a name for a new Blog: ADO.NET Blog
All blogs in the database:
ADO.NET Blog
Press any key to exit...
我的資料在哪裡?
依照慣例,DbContext 已經為你建立了資料庫。
- 如果本地有 SQL Express 實例可用(Visual Studio 2010 預設安裝),則 Code First 已在該實例建立資料庫
- 如果 SQL Express 無法使用,Code First 會嘗試使用 LocalDB(Visual Studio 2012 預設安裝的 LocalDB )
- 資料庫名稱取自導出上下文的完全限定名稱,在本例中為 CodeFirstNewDatabaseSample.BloggingContext
這些只是預設慣例,且有多種方式可以更改 Code First 所使用的資料庫,更多資訊可參考「 DbContext 如何發現模型與資料庫連線 」主題。 你可以用 Visual Studio 的 Server Explorer 連接這個資料庫
檢視 -> 伺服器檔案總管
右鍵點選 「資料連線 」,選擇 「新增連線...」
如果你之前沒從 Server Explorer 連接過資料庫,你需要選擇 Microsoft SQL Server 作為資料來源
取決於你安裝的是 LocalDB 還是 SQL Express,進行相應的連接。
我們現在可以檢視 Code First 所建立的架構。
DbContext 透過檢視我們定義的 DbSet 屬性,決定模型中要包含哪些類別。 接著它會使用預設的 Code First 慣例來決定資料表與欄位名稱、資料型別、尋找主鍵等。 接下來的攻略中,我們將探討如何覆蓋這些慣例。
5. 應對模型變更
現在是時候對模型做一些修改,當我們做這些變更時,也需要更新資料庫結構。 為此,我們將使用一個稱為 Code First Migrations(簡稱 Migrations)的功能。
遷移功能讓我們能有一套有序的步驟,描述如何升級(和降級)我們的資料庫綱要。 這些步驟稱為遷移,包含描述要套用變更的程式碼。
第一步是啟用 BloggingContext 的 Code First 遷移功能。
工具 -> 函式庫套件管理器 -> 套件管理器主控台
在套件管理員主控台執行 Enable-Migrations 指令
我們的專案新增了一個 Migrations 資料夾,裡面包含兩個項目:
- Configuration.cs – 此檔案包含遷移系統用於遷移 BloggingContext 的設定。 這次攻略不需要做任何更改,但這裡可以指定種子資料、註冊其他資料庫的提供者、更改遷移產生的命名空間等等。
- <時間戳_InitialCreate.cs> – 這是你的第一次遷移,代表已經對資料庫所做的變更,讓它從空白資料庫變成包含部落格和文章資料表的資料庫。 雖然我們讓 Code First 自動替我們建立這些資料表,但自從我們選擇啟用遷移功能後,這些資料表已經被轉換為遷移。 Code First 也在我們的本地資料庫中記錄了這次遷移已經被執行。 檔名上的時間戳記用於排序。
現在讓我們對模型做個修改,為 Blog 類別新增一個 URL 屬性:
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public virtual List<Post> Posts { get; set; }
}
- 在套件管理器主控台執行 Add-Migration AddUrl 指令。 Add-Migration 指令會檢查自上次遷移以來的變更,並用發現的任何變更來支撐新的遷移。 我們可以給遷徙一個名字;此時我們稱遷移為「AddUrl」。 支架程式碼說我們需要在 dbo.Blogs 表中加入一個 Url 欄位,可以存放字串資料。 如果需要,我們可以編輯支架上的程式碼,但在這個案例中並非必須。
namespace CodeFirstNewDatabaseSample.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddUrl : DbMigration
{
public override void Up()
{
AddColumn("dbo.Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("dbo.Blogs", "Url");
}
}
}
- 在套件管理主控台執行 Update-Database 指令。 此指令會將所有待處理的遷移套用到資料庫。 我們的 InitialCreate 遷移已經完成,因此接下來的遷移只會套用我們的新的 AddUrl 遷移。 提示:呼叫 Update-Database 時可以用 –Verbose 開關查看資料庫執行的 SQL。
新的網址欄位現在已加入資料庫中的部落格表:
6. 資料註解
目前我們讓 EF 用預設慣例來發現模型,但有時類別不符合慣例,需要進行進一步設定。 這有兩種可能;我們會在這一節介紹資料註解,下一節則介紹流暢的 API。
- 讓我們在模型中加入一個 User 類別
public class User
{
public string Username { get; set; }
public string DisplayName { get; set; }
}
- 我們也需要在導出上下文中加入一個集合
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }
}
- 如果我們嘗試新增遷移,會收到錯誤訊息,說「EntityType 'User' 沒有定義主鍵。請定義此 EntityType 的主鍵。」 因為 EF 無法辨識 Username 應為 User 的主鍵。
- 我們現在要用 Data Annotations,所以需要在 Program.cs 頂部加上 using 語句
using System.ComponentModel.DataAnnotations;
- 以識別它是主鍵,現在在使用者名稱屬性上添加註解。
public class User
{
[Key]
public string Username { get; set; }
public string DisplayName { get; set; }
}
- 使用 Add-Migration AddUser 指令來架構遷移,將這些變更套用到資料庫
- 執行 Update-Database 指令,將新的遷移套用到資料庫
新資料表已加入資料庫。
EF 支援的完整註解列表如下:
- 關鍵屬性
- StringLengthAttribute
- 最大長度屬性
- 並行檢查屬性
- RequiredAttribute
- 時間戳屬性
- ComplexTypeAttribute
- 欄位屬性
- TableAttribute
- InversePropertyAttribute
- ForeignKeyAttribute
- DatabaseGeneratedAttribute
- 非映射屬性
7. 流式 API
在前一節,我們探討了如何利用資料註解來補充或覆蓋慣例偵測到的部分。 另一種配置模型的方法是透過 Code First fluent API。
大多數模型配置可透過簡單的資料註解完成。 流暢 API 是一種更進階的模型配置方式,涵蓋資料註解能做的所有功能,以及資料註解無法實現的進階設定。 資料註解與流暢 API 可同時使用。
要存取 Fluent API,請覆寫 DbContext 中的 OnModelCreating 方法。 假設我們想將 User.DisplayName 儲存在的欄位重新命名為 display_name。
- 用以下程式碼覆蓋 BloggingContext 上的 OnModelCreating 方法
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.Property(u => u.DisplayName)
.HasColumnName("display_name");
}
}
- 使用 Add-Migration ChangeDisplayName 指令來架構遷移,將這些變更套用到資料庫中。
- 執行 Update-Database 指令,將新的遷移套用到資料庫。
DisplayName 欄位現已改名為 display_name:
總結
在這篇操作指南中,我們探討了使用 Code First 開發新資料庫的方式。 我們用類別定義了一個模型,然後用該模型建立資料庫並儲存和檢索資料。 資料庫建立後,我們使用 Code First Migrations 隨著模型演進而改變結構。 我們也看到如何利用資料註解和 Fluent API 來配置模型。