Entity Framework Code First起手入門
Entity Framework從誕生到現在已經有幾個年頭了,初出來的時候其實被大家質疑與Linq To SQL有什麼不同? 是要用來取代掉Linq To SQL嗎?
其實這類的問題在初期常被人問到,但是慢慢的到了現在大家已經開始能夠接受Entity Framework在開發上帶來的便利性,這當然也要歸功於微軟Entity Framework團隊的努力。
Entity Framework到了4.x版之後加入一種新的開發模式- Code First。其實這種開發模式與過往的開發模式有著滿大的差別,特別是直接從Linq To SQL就開始使用的開發人員會有更強烈的感受。在過去,大家已經習慣了先把資料庫的Table Schema先設計好並且執行SQL Script在資料庫中建出資料表,但是Code First卻與之相反,反倒是先撰寫程式再來建立出資料庫。
Entity Framework資定義三種開發模式:
- Database First
- Model First
- Code First
在Database First的開發模式中,我認為較常在需求目標確定或是擴充型系統開發上使用,因為這類的專案或是產品開發其穩定性相對比較高,較不會有太大的變動,特別是變動在Table Schema的部份,除非是需求收集有問題或困難,否則大抵上採用這種開發模式是較能掌控開發節奏的。
Model First我認為通常是建置案在使用居多,但是因為開發習慣的關係,其實我真的較少遇到有人使用Entity Framework的Model First開發模式。
而最後一個就是本篇文章要討論的開發模式-Code First。採用這種模式通常是應用在時程極短但是變動性卻又很大的專案上,特別是針對需求收集不易或是客戶完全不知道要做什麼系統的狀況下。
Code First的開發
在開發Code First上,基本上我認為有幾個步驟:
1. 定義出PO
2. 決定該PO與資料庫的對應
3. 開發DataContext
定義出PO這個步驟是所有Code First在開發上的根本,若拿DataBase First開發模式來說就相當於是在設計Table Schema一般,但是在設計上要注意的是,不要和Table Schema設計混淆了,在設計時腦袋中的模式應當是採用物件導向系統設計的觀點來設計,因為這些PO是系統在運作上的關鍵物件而不是資料表。
只要是ORM我私心感受都是為了要切斷和隔絕物件導向設計與資料庫設計,但是有一個現實上的問題就是最終PO身上的資料還是要放入資料庫中,是故,第二個步驟也是一個重要的環節。
最後一個步驟單純就是為了Entity Framework能夠運作所做的,因為DataContext就是Entity Framework在開發時會使用到的類別。
先來定義出一個簡單的PO:
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int? Age { get; set; }
}
這個PO實在是再簡單不過了;我定義了一個PO名叫: Person, 且它擁有三個屬性: ID, Name, Age。
再來就是要進入第二步驟,不過在本篇中先採用比較簡單的方式來定義與資料庫的對應- Data Annotation。
[Table("Persons")]
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
public string Name { get; set; }
public int? Age { get; set; }
}
其實Entity Framework定義了很多的Data Annotation提供給開發人員使用,但是還是有些東西有缺憾而這個部份在未來的文章中會提到如何補足Default, Constraint, Function…等。
這上面這一段程式中我一共用了三個Data Annotation: Table, Key, DatabaseGenerated。相信大家從字面上不難猜到各個Data Annotation的意義;Table內的參數是在指定當這個PO與資料庫作對應時是與Persons這張資料表作對應,Key當然就是指定Primary Key了,而最後一個則是告知資料庫PO中ID這個欄位在資料表上與之對應的欄位是採用Identity的方式給值。
public class SampleContext:DbContext
{
public DbSet<Person> Persons { get; set; }
public SampleContext()
:base("name=SampleContext")
{
Database.SetInitializer(new CreateDatabaseIfNotExists<SampleContext>());
}
}
在上述程式中比較特別的部份在於建構子中的內容,而建構子的那一行程式碼是在告知Entity Framework當要建立這個資料庫的時候該採用什麼的建立模式?
其模式一共有三種可以選擇:
1. DropCreateDatabaseIfModelChanges
2. DropCreateDatabaseAlways
3. CreateDatabaseIfNotExists -本範例所採用的
同樣的,我相信從字面上大家應該就能夠瞭解它所代表的行為了。(真是優良的命名XD)
在建構子中,還有傳遞了一個參數給父類別,這是為了指定要採用Web.config中所定義的連線字串中那一個。
<connectionStrings>
<add name="SampleContext"
connectionString="Data Source=.;Database=Sample;Integrated Security=True;"
providerName="System.Data.SqlClient" />
</connectionStrings>
編譯之後執行,當使用者第一次操作到會使用到Entity Framework的程式碼部份時,就在這個時候Entity Framework會自動在指定的資料庫系統上建立一個資料庫-Sample,並且該資料庫中還有一張資料表-Persons。
最後附上常用Data Annoation。
表1. Entity Framework常用的Data Annotation
| Data Annotation | 說明 |
| Table | 宣告資料表名稱 |
| Key | 宣告主索引鍵 |
| Required | 宣告為必填欄位 |
| MaxLength | 允許陣列或字串的最大長度 |
| MinLength | 允許陣列或字串的最小長度 |
| NotMapped | 該欄位不與資料表欄位對應 |
| ComplexType | 自定義的複雜型別供欄位使用 |
| ConcurrencyCheck | 該欄位在修改/刪除操作時,檢核同步異動衝突 |
| TimeStamp | 功能同ConcurrencyCheck,但僅能用在型別為Byte[]的屬性上 |
| Column | 提供更方便的欄位對應;欄位名稱/型別/順序 |
| ForeignKey | 資料表之間具有關聯性時,需要能夠Navigate到另一張表時使用 |
表1中列出一堆東西,但是實際上的用法待之後的文章一一作個範例介紹