EF外鍵
EFCode First 導航屬性
首先談談自己對EF的接觸的過程吧,最先接觸EF只是因為EF支持從數據庫把關系扒下來,可以省掉自己寫Select、Update、Insert這些SQL語句,而且修改非常方便,后來在使用的過程中發現導航屬性這個關系,然后才慢慢知道數據庫的索引是什么,由於自己接管的是大學生社團的數據庫,大多時候創建者並不會考慮表的聯系,一般創個主鍵就完事了(順便吐槽一句,握草,數據庫的表名和列名是什么鬼全用拼音首字母,為了兼容前面的內容我們還得花一半時間猜你們的列名,簡直醉了,除了ID這個英文他們會,你們的英語是體育老師教的嗎???)言歸正傳,用EF的確學到了對數據庫表的的建立的理解,畢竟自己剛學數據庫的時候就是把所有的字段塞到一張表里面,剛開始自己使用EF從數據庫拔下來的表然后修改實體的關系的數據(感覺其實就是使用EF的EMDX的Code First),使用這個並沒有出現很多問題,后來又接觸完整的Code First,就是直接用代碼生成數據庫,雖然中間遇到無數的BUG但是這些BUG讓我對數據庫和EF的關系有了更深的理解,話不多說,直接上BUG。
1. EF未能確定外鍵,請用注解屬性或Fluent API標記外鍵
網上關於如何用代碼的(Fluent API或注解屬性)指定外鍵的文章有很多有很多。在這里我想談談對外鍵的理解,首先建立起一張主表
主表
列名 | 類型 |
---|---|
ID | int |
Name | nvarchar(50) |
首先ID是獨一無二的,而Name不是(重名的有很多),當我們給ID套上主鍵的時候,這時候插入這張表的ID只能有一種(這是數據庫的一種約束,當然你可以不選擇這種約束),一個人除了姓名還有其他東西,假如這時我們還有幫他加入性別這個信息,我們可以修改上一張表添加一個字段,也可以新建一張表存貯性別這個信息(當然在實際生活中只用一張表存一個信息很少),我們新建的這張表是這樣的,
附表
列名 | 類型 |
---|---|
Sex | bit |
這張表存貯了性別這個信息,但是如何將他從主表聯系起來呢,我們先提取主表中的ID作為聯系(我們稱為外鍵)表改為
附表
列名 | 類型 |
---|---|
ID | int |
Sex | bit |
我們把列名ID設為主鍵,這樣我們就建立了一對一的關系,這個附表的ID必須不為空,這種關系還有一種就是將外鍵存貯在主表里面,就是將主表里面添加一個外鍵SexID,主表和附表要改成下面這種
主表
列名 | 類型 |
---|---|
ID | int |
Name | nvarchar(50) |
SexID | int |
附表
列名 | 類型 |
---|---|
Sex | bit |
現在這種結構就是外鍵SexID可以為空(注上面的外鍵不能為空),
ps:說到外鍵不能為空我插一句,有些教科書上說外鍵不能為空也是對的,外鍵只是一個列名,當這個列名不唯一(也就是不為主鍵的時候)這是外鍵可以為空,為空的含義是不確定對應主表的值。
現在開始談談這種情況在EF發生的原因,你吧主表設為Person表,附表為SexInfo表,對應的代碼如下
public Person{
public int ID{get;set;}
public string name{get;set;}
public virtual SexInfo Sex{get;set;
}
public SexInfo{
public int ID{get;set;
public bool Sex{get;set;
public Person person{get;set;}
}
這個時候EF無法判斷哪個是主表那個是附表,就是無法將外鍵加在哪個表的ID上,或者像上面的表中在Person表中添加一個外鍵。也就是在這種情況里面有四種可能的情況
- 在Person表里面添加一個外鍵(假設為Person_SexInfoID)
- 將Person表中的ID設為主鍵和外鍵
- 在SexInfo表中添加一個外鍵(假設為SexInfo_PersonID)
- 將SexInfo表中的ID設為主鍵和外鍵。
注假設在EF中沒有給屬性添加[Key]注解屬性或在Fluent API中聲明一個屬性為主鍵的話,EF會自動將有ID后綴的屬性設置為主鍵並讓他為標志字段自增,還有表中沒有主鍵無法導入到EF中。
雖然EF有自動檢測代碼生成關系,但是本人還是比較推崇自己在Code First時就想好外鍵,這樣在用模型綁定的時候就不會發生一些很可能發生的錯誤。在這張表里面為了節約數據庫空間最好在SexInfo里面添加一個外鍵,現在我就來談談分別在兩個表里面添加外鍵可能會遇到的BUG。
- 在SexInfo里面添加外鍵PersonID
類修改成為
public Person{
public int ID{get;set;}
public string name{get;set;}
public virtual SexInfo Sex{get;set;
}
public SexInfo{
public int ID{get;set;
public bool Sex{get;set;
public int PersonID{get;set;}
public Person person{get;set;}
}
然后我們可以選擇在PersonID上加上[ForeignKey("Person")]
和[Requird]
,或者在重寫的OnModelCreating方法中加入 這樣一句代碼
modelBuilder.Entity<SexInfo>().HasRequired(x => x.Person)
.WithRequiredPrincipal(x => x.BindingRole).HasForeignKey(x => x.MenusManageID)
其實我更推崇寫Fluent API 來約束,因為將注解屬性放在Model里面太亂而且容易錯,比如說假如你在PersonID上面少注釋了一個[Required]
你又會得到一個模型驗證錯誤,這個BUG是隱藏的最深的,現在來重點提一提這個BUG
BUG:模型驗證錯誤····多重性與關系“········”中 Role“··············”中的引用約束沖突。因為 Dependent Role 中的所有屬性都不可以為 null,Principal Role 的多重性必須為“1”。
這個錯誤一般出現在自己聲明外鍵的時候,網上的資料介紹的不多,外國資料上我看過好像有人說是因為EF不允許自己聲明外鍵,我差點就相信了····,這個BUG告訴我們依賴關系中不能為null,但是什么是依賴關系和委托關系,其實這個告訴我們的是兩種關系,一種是(從屬關系)Dependent Role另一種是(主體關系)Principal Role,這兩種關系就是上面我們提到的外鍵可不可以為空,由於我們給person或PersonID沒有加上Required
屬性,EF系統給我們外鍵套上的是可空的字段,但是我們給的外鍵PersonID是int型,int是值類型,在C#里面值類型不能為空(如果沒有初始化時為0),所以EF報錯,你要么給外鍵加上Required
標記指定它必須存在,要么給一個可為空的int型,像這個示例里面外鍵PersonID是必須的,然后有些對應是0-1 對 1,所以這時候就疑惑了我們怎么給外鍵賦值,我們有一種辦法命名一種類型他的值可以int也可以為空,但是EF會認識我們這種獨特的外鍵嗎?還好EF早想到了這點,有一種泛型可以為空也可以為你想要的類型,這種就是Nullable<T>
,在這個方法中我們只要將外鍵PersonID的類型換成 這個
public Nullable<int> PersonID{get;set;}
這就是解決0-1對1的方法,我們在設計數據庫結構時要考慮到C#自己本身與數據庫類型的對應,C#是一種強類型語言,如果我們不注意這點,程序就會崩潰。當然EF導航屬性並不僅僅是1對1,還有1對多和多對多,當我們想用0去對應的話,這個外鍵的類型就要考慮考慮了。
還有一個比較常見的BUG吧,來提一提。
BUG:······: 引用約束的 Dependent Role 中所有屬性的類型都必須與 Principal Role 中相應的屬性類型相同。引用約束“·····”中,實體“····”的屬性“····”的類型與實體“·····”的屬性“·····”的類型不匹配。
這個bug就是相對應主體和外鍵不匹配的情況,相對應的類如下
public Person{
public long ID{get;set;}
public string name{get;set;}
public virtual SexInfo Sex{get;set;
}
public SexInfo{
public int ID{get;set;
public bool Sex{get;set;
public int PersonID{get;set;}
public Person person{get;set;}
}
Person里面的主鍵我改成了long型,然而外鍵PersonID卻是int型,出現這個錯誤是對外鍵的認識還不夠,外鍵其實就是主鍵的“分身”,主鍵是long型,外鍵必須也是long型,同理主鍵是int型外鍵也必須是ing型,
ps:導航屬性是指對象,比如說Person類實例person,而外鍵是指存貯在數據庫里面的一個特殊的列名。
充分認識導航屬性和外鍵是搭建一個扎實的數據庫結構的基礎,在學習和應用EF的過程中也是了解數據庫的結構的學習過程,EF或許在運行速度方法上比一般的SQL語句要慢,但是用EF我們可以更加方便的搭建一個好的數據體系,搭建一個好的數據體系可以讓你在完成項目的時候事半功倍。