前面,我們已經了解了Code-First利用領域類,怎么為我們創建數據庫的簡單示例。現在我們來學習一下Code-First約定吧。
-
什么是約定
約定說白了,就是基于一套規矩辦事,這里就是基于你定義好的領域類,然后根據默認的規矩來配置概念模型。Code-First約定定義在這個命名空間下面:
System.Data.Entity.ModelConfiguration.Conventions
現在來大致瀏覽一下都有哪些約定吧:
-
類型發現
在前面的章節中,我們在上下文類中創建了DbSet屬性的類集合,然后Code-First會根據這個DbSet屬性為我們創建數據表。
codeFirst會為我們包含任何引用類型到這些類集合中,甚至盡管這寫引用類型定義在不同的程序集中。
例如:下面的代碼中,Student實體包含了Teacher類的引用,然而上下文類總,卻并未包含Teacher的類型的DBSet屬性。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { public class Student { public int StudentId { get; set; } public string StudentName { get; set; } public DateTime DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } public Standard Standard { get; set; } public Teacher Teacher { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace EF1 { public class Teacher { public int TeacherId { get; set; } public string TeacherName { get; set; } } }
數據上下文類中,并沒有包含Teacher類型的DbSet屬性:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { public class DbContextClass:DbContext { public DbContextClass() : base("ConnectionString") { } public DbSet<Student> Studnets { get; set; } public DbSet<Standard> Standards { get; set; } } }
然后我們運行程序,運行成功之后,打開數據庫,發現:
哈哈,是不是報錯了?
具體的錯誤消息是:The model backing the 'DbContextClass' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269)
這是因為,我們改了實體,沒有開啟數據庫遷移特性,不過還有另外一種解決辦法,我這里,
我們改一下上下文類:
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { public class DbContextClass:DbContext { public DbContextClass() : base("ConnectionString") { } public DbSet<Student> Studnets { get; set; } public DbSet<Standard> Standards { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DbContextClass>()); base.OnModelCreating(modelBuilder); } } }
這里改動的語句:
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<DbContextClass>());
意思是,當模型發生改變的時候,就刪掉重新創建數據庫。
現在我們運行程序,發現就能運行成功了。
然后我們看一下數據庫:
多了一個Teacher表。
看一下Teacher的表結構:
然后看一下Student表的表結構:
Code-First可以推斷類,盡管上下文中并沒有包含Teacher的屬性。
所以,我們總結一下類型發現的約定:
- Code-First包含類型定義也就是DbSet屬性在上下文類中;Code-First includes types defined as a DbSet property in context class.
- Code-First包含引用類型在實體類中,盡管他們定義在不同的程序集下;Code-First includes reference types included in entity types even if they are defined in different assembly.
- Code-First包含派生類,盡管這個類要定義成DbSet屬性。(Code-First includes derived classes even if only the base class is defined as DbSet property.)
-
主鍵約定
在之前的例子中,我們看見了Code-First為每個表都創建了主鍵,這個默認的規則就是,類名Id(大小寫不敏感,即不區分大小寫)。這個主鍵的類型可以是任何類型,但是如果主鍵的類型是numeric 或者GUID,它將會被配置成自增列。
然后,如果你定義主鍵的屬性名字不是Id或者不是類名+ID,然后在運行的時候,就會報錯。
我們看看下面的代碼就知道了:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { public class Student { public int StdID { get; set; } public string StudentName { get; set; } public DateTime DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } public Standard Standard { get; set; } public Teacher Teacher { get; set; } } }
改完之后,我們運行程序,就會報錯:
具體的錯誤消息:
One or more validation errors were detected during model generation:
EF1.Student: : EntityType 'Student' has no key defined. Define the key for this EntityType.
Studnets: EntityType: EntitySet 'Studnets' is based on type 'Student' that has no keys defined.
意思是沒有定義主鍵》》》
如果你想要StdID成為主鍵,可以使用數據注解或者Fluent APIS,我們在后面將會學到。
-
關系約定
通過導航屬性,Code-First能夠推斷出,兩個實體之間的關系,這個導航屬性可以是簡單的引用類型或者是集合類型,例如,我們在Student實體中定義了Standard導航屬性,然后我們在Stanrard實體中定義了Icollection<Student>導航屬性,所以,Code-First將會自動為我們創建一對多的關系。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { public class Student { public int StdID { get; set; } public string StudentName { get; set; } public DateTime DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } /// <summary> /// 導航屬性 /// </summary> public Standard Standard { get; set; } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace EF1 { public class Standard { public int StandardId { get; set; } public string StandardName { get; set; } /// <summary> /// 集合類型的導航屬性 /// </summary> public ICollection<Student> Students { get; set; } } }
然后我們運行程序成功之后,看到數據庫:
Code-First約定,為我們創建了外鍵
<navigation property Name>_<primary key property name of navigation property type>
e.g. Standard_StandardId.
-
外鍵約定
我們已經知道了,當我們添加一個外鍵屬性的時候,Code--First會我們創建一個外鍵。推薦做法是,包含一個外鍵屬性的依賴關系。
請看下面的代碼;
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { public class Student { public int StudentId { get; set; } public string StudentName { get; set; } public DateTime DateOfBirth { get; set; } public byte[] Photo { get; set; } public decimal Height { get; set; } public float Weight { get; set; } /// <summary> /// 導航屬性 /// </summary> public Standard Standard { get; set; } /// <summary> /// Foriegn key for Standard /// </summary> public int StandardId { get; set; } } }
這樣改動之后,我們在運行程序,發現怎么了?是不是又報錯了?
這個是因為,外鍵表Standard中沒有數據。
我們往Standard表中,插入一條數據,然后改一下Main函數里面的代碼:再運行程序>>>>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace EF1 { class Program { static void Main(string[] args) { Student stu = new Student() { StudentId = 1, StudentName = "韋小寶", Height = 172, Weight = 120, DateOfBirth = DateTime.Now,StandardId=1}; using (var db = new DbContextClass()) { db.Studnets.Add(stu); db.SaveChanges(); } Console.WriteLine("添加成功"); Console.ReadKey(); } } }
運行之后,是不是沒有出錯了?
現在讓我們再來看一下數據庫:
發現什么變化了么?是不是這個外鍵的名字不再是以外鍵屬性的名字+外鍵實體的主鍵ID命名了?
并且你注意到了沒有,這個外鍵屬性也是不能為空的?
因為int類型是不能為空的!!
code-Forst可以基于外鍵屬性的【為空性】(nullability )推斷出多重性的關系,如果這個屬性是可以為空的,然后這個關系可以看成是null,否則的話,就是not null,你可以修改
StandardId屬性的類型從int變成Nullable<int>,然后運行程序的話,得到的外鍵就是可以為空了。
-
復雜類型約定
Code-First可以為類創建復雜類型,可以不包含主鍵屬性,并且不使用數據注解和Fluent APIs來注冊主鍵
上面這寫就是Code-First約定的大概內容了,這些約定可以被打破,通過使用數據注解和Fluent APIs,在EF6中,你可以自定義約定。
附上系列目錄:
- 什么是Code First
- 簡單的Code First例子
- Code-First 約定
- DB Initialization(數據庫初始化)
- Inheritance Strategy(繼承策略)
- Configure Domain Classes(配置領域類)
- DataAnnotations(數據注解)
- Fluent API
- Configure One-to-One(配置一對一關系)
- Configure One-to-Many(配置一對多關系)
- Configure Many-to-Many(配置多對多關系)
- Move Configurations(數據遷移)
- DB Initialization Strategy(數據庫初始化策略)
- ...待續.....
文章列表