寫在前面:html
EF 中 Code First 的數據遷移網上有不少資料,我這份並沒什麼特別。Code First 建立視圖網上也有不少資料,但好像很麻煩,並且親測好像是無效的方法(多是我太笨,沒搞成功),我摸索出了一種簡單有效的方法,這裏分享給你們。web
EF是Entity Framework(實體框架)的簡寫,是微軟出品的用來操做數據庫的一個框架,會ASP.NET MVC的朋友對他確定都不陌生。因爲學藝不精,我對EF存在一疑慮,就不以【提問】的方式來問了,我以【總結】的方式來表達,若是總結有誤的地方,還請看到的大神能夠指正,並賜教我正確的認知,萬分感謝。數據庫
EF有三種使用方式:服務器
1) Db First 數據庫優先網絡
2) Model First 模型優先app
3) Code First 代碼優先框架
上圖中,前三種分別是DbFirst、ModelFirst和CodeFirst,而第4種也是CodeFirst。ide
1、DbFirst、ModelFirst必須從app.config/web.config中讀取鏈接字符串,簡直無情函數
不知道是我功底太差,仍是微軟真的就是這麼設計的,我發現一個問題:DbFirst和ModelFirst這兩種模式,竟然只能從App.config/Web.config中讀取數據庫鏈接字符串,這是一個致命的問題,這意味着鏈接字符串不能被加密必須暴露給客戶看(若是你是窗體應用程序的話)。(感謝 @lcs-帥 指正這一點) 這意味着咱們必需要 這樣(利用ASP.NET加密和解密Web.config中鏈接字符串) 來對咱們的鏈接字符串進行加密,好像有點太複雜了。學習
若是不利用上面紅色大字中提到的方法,那咱們大概能夠有兩種方式讀取數據庫鏈接字符串,第一種是在App.config/Web.config中讀取:
1 <connectionStrings> 2 <add name="Model1" connectionString="data source=(LocalDb)\v11.0;initial catalog=數據庫名;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="..略.." /> 3 </connectionStrings>
這種方式不太好,由於若是是窗體應用程序的話,等於就用明文告訴了別人你的數據庫密碼了。固然,你也能夠對這一段字符串進行加密,就像這樣:
1 <connectionStrings> 2 <add name="Model1" connectionString="ZGF0YSUyMHNvdXJjZSUzRCUyOExvY2FsRGIlMjklNUN2MTEuMCUzQmluaXRpYWwlMjBjYXRhbG9nJTNEJXU2NTcwJXU2MzZFJXU1RTkzJXU1NDBEJTNCaW50ZWdyYXRlZCUyMHNlY3VyaXR5JTNEVHJ1ZSUzQk11bHRpcGxlQWN0aXZlUmVzdWx0U2V0cyUzRFRydWUlM0JBcHAlM0RFbnRpdHlGcmFtZXdvcms=" providerName="..略.." /> 3 </connectionStrings>
而當你像上面這樣對數據庫鏈接字符串進行加密,此時你就等同於選擇了使用第二種方式讀取鏈接字符串了,第二種讀讀取數據庫鏈接字符串的方式是:
1 string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["Model1"].ConnectionString; //可選 2 connStr = 解密(connStr); //可選 3 Model1 db = new Model1(connStr); //第二種設置數據庫鏈接字符串的方式
這第二種方式的前2句代碼是可選的,也就是說你不必定要從Web.config/App.config中讀取數據庫鏈接字符串,你也不必定要對數據庫鏈接字符串進行加解密。
這第二種方式最重要的是第3句代碼,也就是說你可使用一個字符串變量來作爲數據庫鏈接字符串,這意味着你不必定要從Web.config/App.config中讀取數據庫鏈接字符串。
然而!惋惜的是:在DbFirst和ModelFirst中,並不支持第二種設置數據庫鏈接字符串的方式,這也就意味着DbFirst和ModelFirst的應用場景大打折扣!
在DbFirst或ModelFirst中,調用如下代碼會報錯,由於只有一個無參構造方法:
也許你(曾經我)自作聰明的想到一些辦法,寫一個【部分類】爲 DbContext 添加一個有參構造函數:
事實證實是自作聰明,當你去操做任何一張表的時候,就報這個錯誤:
當你(我)看到這一個錯誤提示的時候,終於認命了:在DbFirst和ModelFirst中,數據庫連按字符串只能寫在App.config/Web.config中,簡直無情。
2、CodeFirst 能夠從代碼中讀取數據庫鏈接字符串,沒必要從Web.config/App.config中讀取。
要在CodeFirst中對鏈接字符串進行加密,首先要添加一個帶參構造函數,就像這樣:
1 public Model1() 2 : base("name=Model11") 3 { 4 } 5 public Model1(string nameOrConnectionString) 6 : base(nameOrConnectionString) 7 { 8 }
而後就這樣:
1 string connStr = System.Configuration.ConfigurationManager.ConnectionStrings["Model1"].ConnectionString; //可選 2 connStr = 解密(connStr); //可選 3 Model1 db = new Model1(connStr); //第二種設置數據庫鏈接字符串的方式
從配置文件中讀取出加了密的鏈接字符串,再進行解密,最後應用於 DbContext 之中。
在CodeFirst中進行這樣的操做程序不會報錯,簡直有愛。
3、【空 Code First 模型】與【來自數據庫的 Code First 】異曲同工,是同樣的
關於Code First,有兩個能夠選擇,下圖中的最後兩個:
這二者是異曲同工,是同樣的。
【空CodeFirst模型】就是新建一個空的模型,而後本身寫實體類代碼,根據這些實體類代碼來生成數據庫,生成數據庫了以後,就與【來自數據庫的CodeFirst】是同樣的。
【來自數據庫的CodeFirst】就是根據從數據庫中選擇的一些表/視圖來生成實體類代碼,這些自動生成的代碼和你本身手寫是同樣同樣的,也就是與【空CodeFirst模型+手寫實體類代碼】是同樣的。
然而,並不徹底是!慢慢聊吧。
4、數據遷移
數據遷移,就是實體模型類發生變化以後(好比多一個字段,字段類型改變,或者多了一個實體類),數據庫的表結構也跟着一塊兒變化的過程。
4.1 爲何要使用數據遷移
由於模型和實際的數據庫結構可能會不同,不同的話程序運行就會有問題,因此須要使用數據遷移這個功能來幫助咱們解決問題。
用一個例子來講明,建立控制檯應用程序,建立【空CodeFirst模型】:
1 //數據庫Model1 2 public class Model1 : DbContext 3 { 4 public Model1() : base("name=Model1") { } 5 public virtual DbSet<MyEntity> MyEntities { get; set; } 6 } 7 //實體類 (對應表:MyEntities) 8 public class MyEntity 9 { 10 public int Id { get; set; } 11 public string Name { get; set; } 12 }
修改數據庫鏈接字符串,改爲一個咱們熟悉的數據庫服務器名,以避免數據庫新建了以後,找不到在哪裏:
<add name="Model1" connectionString="server=服務器名;uid=用戶名;pwd=密碼;database=新的數據庫名;" providerName="System.Data.SqlClient" />
而後在Main方法中隨便訪問一下這個實體類:
1 using (var db = new Model1()) 2 { 3 //隨便訪問一下 MyEntities 表,以便讓 EF 自動建立此表 4 var x = db.MyEntities.Find(1); 5 }
運行程序,就會獲得一個新的數據庫,新數據庫裏面有一個新表 MyEntities。(還有另外一個叫 __MigrationHistory 的表,是用於記錄代碼遷移的歷史記錄,也是代碼遷移模式的開關,後面慢慢聊)
到這裏爲止,一切都正常。
如今,咱們再添加一個年齡Age字段:
1 //實體類 (對應表:MyEntities) 2 public class MyEntity 3 { 4 public int Id { get; set; } 5 public string Name { get; set; } 6 //添加了年齡字段 7 public int? Age { get; set; } 8 }
這時,再運行程序,就會報一個錯誤:
錯誤的大概意思就是:模型與實際數據庫結構不一致,須要使用 Code First 中的數據遷移功能來解決問題。
4.2 數據遷移的用法
當模型與實際數據庫結構不一致時,就須要使用數據遷移。數據遷移咱們只須要掌握三個方法就能夠了,它們分別是:
1) 在當前項目中啓用數據遷移 Enable-Migrations
2) 添加遷移版本 Add-Migration 版本名稱
3) 更新數據庫 Update-Database
首先第一步,須要在當前項目中啓用數據遷移,打開【視圖->其它窗口->程序包管理器控制檯】快捷鍵是 Alt+V,E,O,而後輸入命令:
Enable-Migrations
這樣就能夠在當前項目中啓用數據遷移了,其效果是項目中會多出 Migrations 文件夾以及一些代碼:
Migrations 這個文件夾就是數據遷移的相關文件夾,作爲初學者,不要亂動裏面的代碼。
因爲咱們的模型已經發生了改變,因此須要添加一個新的遷移版本,在程序包管理控制檯執行以下命令:
Add-Migration v1
其中的v1表示版本號,本身隨便取一個名字就能夠了。此時,項目中多出一個文件:
這個文件的代碼是:
1 //版本 v1 2 public partial class v1 : DbMigration 3 { 4 //版本升級 5 public override void Up() 6 { 7 //向 MyEntities 表添加 Age 字段 8 AddColumn("dbo.MyEntities", "Age", c => c.Int()); 9 } 10 11 //版本降級 12 public override void Down() 13 { 14 //向 MyEntities 表刪除 Age 字段 15 DropColumn("dbo.MyEntities", "Age"); 16 } 17 }
代碼中的註釋是我寫上去的,大概的爲你們解釋了一下是什麼意思。到這裏爲止,數據庫依然沒有發生變化。
想要數據庫發生變化,多出一個 Age 字段,那就要用到第三個命令:
Update-Database
執行成功以後,數據庫就多出一個 Age 字段了。
此時,再運行程序,程序就不會報錯了。由於此時的模型與實際數據庫結構已經一致。
至此,例子已演示完畢。
這裏有一個很是很是很是重要的細節,要注意:
若是你使用了 new Model1(connStr) 自定義數據庫鏈接字符串,在使用數據遷移的命令時,所訪問的數據庫是你 web.config/app.config 中配置的數據庫,並非你自定義鏈接的那個數據庫。
這是一個巨坑,必定要注意:
使用了自定義數據庫鏈接字符串時,雖然 web.config/app.config 中的明文數據庫鏈接字符串並不必定要被髮布,但在開發時也必定要在裏面填寫上正確的數據庫鏈接信息。
5、關閉數據遷移
數據遷移的開啓與關閉分兩個層面來講:
1) Visual Studio 項目是否開啓了數據遷移
2) SQL Server 數據庫是否開啓了數據遷移
對於項目中是否開啓了數據遷移,識別方法是:有沒有 Migrations 這個文件夾以及裏面的類文件。若是有就表示開啓了,沒有就沒開啓。
對於數據庫中是否開啓了數據遷移,識別方法是:有沒有 __MIgrationHistory 這個表。若是有就表示開啓了,沒有就沒開啓。
如下討論的數據遷移開啓和關閉,都是指數據庫中數據遷移的開啓和關閉。
實際上 Code First 又有兩種分支用法:
1) 數據庫中 啓用了數據遷移的 Code First
2) 數據庫中 關閉了數據遷移的 Code First
這個開關並不在於你的項目中有沒有 Migrations 這個文件夾,而在於:你的數據庫裏面,有沒有 __MigrationHistory 這個表。
當你使用【空 Code First 模型】建立出來的數據庫,默認就是開啓了數據遷移的,關閉它的方法天然就是刪除 __MigrationHistory 這個表。
而你使用【來自數據庫的 Code First】默認這個數據庫就是關閉了數據遷移的 ,用一個例子來講明一下吧。
示例:
先確保你的數據庫實例中有一個數據庫,這個庫裏面有一些表,好比 Student 表,而且這個庫沒有 __MigrationHistory 這個表。
而後新建控制檯程序,並建立【來自數據庫的 Code First】,選擇 student 表,自動產生模型代碼和上下文類的代碼:
1 //這是自動產生的代碼 (我進行了刪減) 2 public partial class Model1 : DbContext 3 { 4 public Model1(): base("name=Model1") { } 5 public virtual DbSet<student> student { get; set; } 6 }
1 //這是自動產生的代碼 (我進行了刪減) 2 public partial class student 3 { 4 public int id { get; set; } 5 public string name { get; set; } 6 }
而後在 Main 方法裏隨便調用一下,看看有沒有問題:
1 using (var db = new Model1()) 2 { 3 //隨便調用一下,看看有沒有問題 4 var x = db.student.Find(1); 5 }
由於此時的模型與實際數據庫結構是同樣的,運行結果固然不會有問題了。
如今,咱們修改一下模型,在學生實體類中添加一個身份證的字段:
1 public partial class student 2 { 3 public int id { get; set; } 4 public string name { get; set; } 5 //添加身份證 idcard 字段 6 public string idcard { get; set; } 7 }
此時,咱們再運行程序,程序報錯了,但報的錯誤並非讓咱們進行數據遷移:
報的錯誤是:列名 'idcard' 無效。
這就是數據庫中關閉了數據遷移的 Code First 的行爲表現,不會提示你(要求你)進行數據遷移。
沒有了數據遷移,咱們怎樣才能讓模型和實際數據庫表結構同樣呢?
很簡單,你本身手動在數據庫 student 表中添加一個 idcard 字段不就能夠了嗎。
確實,當咱們手動添加了 idcard 字段以後,再運行程序,程序就不報錯了。
小總結:
1) 使用【空 Code First 模型】建立出來的數據庫默認是開啓數據遷移的,咱們只須要刪除 __MigrationHistory 表就能夠關閉數據遷移。
2) 使用【來自數據庫的 Code First】這種方式,數據庫默認就是關閉了數據遷移的。
3) 在數據庫中關閉數據遷移的優勢是:
你可能並不熟悉數據遷移是個什麼鬼,那不如就讓它見鬼去吧。關閉它,會讓你進入你熟悉世界,經過手動改寫實體類,手動改數據庫表結構,來同步模型與實際表結構,挺好!
6、在關閉數據遷移的模式下,在 EF 實體模型中建立視圖
數據庫關閉了數據遷移的狀況下,要在 EF 實體模型中建立視圖,實在是太簡單了。
你就編寫一個實體類來當成視圖,好比 v_student,你就把它當成是一張表同樣來編寫:
1 //我想寫一個視圖,先別急,先當它是一張表 2 public partial class v_student 3 { 4 public int id { get; set; } 5 public string name { get; set; } 6 //姓 7 public string first_name { get; set; } 8 }
1 public partial class Model1 : DbContext 2 { 3 public Model1(): base("name=Model1") { } 4 public virtual DbSet<student> student { get; set; } 5 //我想寫一個視圖,先別急,先當它是一張表 6 public virtual DbSet<v_student> v_student { get; set; } 7 }
而後,在 Main 方法中訪問這個視圖,看看有沒有問題:
1 using (var db = new Model1()) 2 { 3 //訪問一下這個視圖,看看有沒有問題 4 var x = db.v_student.Find(1); 5 }
運行。固然會有問題啦,會報這個錯誤:
注意到了嗎,它只是說 v_student 無效,並無說讓咱們作什麼數據遷移。
好吧,難道咱們要傻傻的去建立一個 v_student 表嗎? 難道不能夠建立一個 v_student 視圖?
固然能夠,咱們來建立一個視圖吧,在數據庫中執行如下SQL語句:
1 create view v_student 2 as 3 select id,name,substring(name,1,1) first_name from student 4 go
而後,測試運行程序,程序不報錯了。任務完成。
7、在開啓據遷移的模式下,在 EF 實體模型中建立視圖
也許你比較欣賞在數據庫中開啓了數據遷移的這種模式,在數據庫中開啓了數據遷移的模式下,能不能建立視圖呢?若是能夠,又如何建立視圖呢?
網絡上你能夠搜索到不少 Code First 中建立視圖的方法,這些方法都是指數據庫中開啓了數據遷移的 Code First 下的方法,但好像都挺麻煩的,反正我照着作並無作出來。
而後我摸索了一下,找出一種簡單又實用的方法,這裏分享給你們。沒錯,這一段落纔是本文最有價值的部分。
依然用例子來講明:
上接第四節【數據遷移】的例子,咱們來添加一個 V_MyEntity 的視圖,代碼以下:
1 //我想寫一個視圖,先別急,先當它是一張表 2 public class V_MyEntity 3 { 4 public int Id { get; set; } 5 public string Name { get; set; } 6 //姓 7 public string First_Name { get; set; } 8 }
1 public class Model1 : DbContext 2 { 3 public Model1() : base("name=Model1") { } 4 public virtual DbSet<MyEntity> MyEntities { get; set; } 5 //我想寫一個視圖,先別急,先當它是一張表 6 public virtual DbSet<V_MyEntity> V_MyEntity { get; set; } 7 }
而後在 Main 方法隨便訪問一下這個視圖,看看可不能夠:
1 using (var db = new Model1()) 2 { 3 //訪問一下 V_MyEntity 視圖,看看可不能夠 4 var x = db.V_MyEntity.Find(1); 5 }
運行程序,天然是報錯了:
由於模型與實際數據庫表結構不一致,因此報錯了,因爲數據庫中開啓了數據遷移的,因此會提示你(要求你)進行數據遷移。
此時,若是按照正常的步驟來進行數據遷移,咱們將獲得 V_MyEntity 這個表,這固然不是咱們想要的結果,咱們是要 V_MyEntity 視圖啊。
那就須要在數據遷移的過程當中,作一點手腳,具體以下。
在程序包管理器控制檯中輸入如下命令,以建立一個新的遷移版本:
Add-Migration v2
其中的 v2 表示版本號,其實能夠隨便亂寫也行,只是個版本名字而已。
剛剛的操做使咱們多出一個文件,內容以下(其中的中文註釋是我寫的,不是生成的):
1 //代碼遷移版本:v2 2 public partial class v2 : DbMigration 3 { 4 //版本升級 5 public override void Up() 6 { 7 //建立表 8 CreateTable( 9 "dbo.V_MyEntity", 10 c => new 11 { 12 Id = c.Int(nullable: false, identity: true), 13 Name = c.String(), 14 First_Name = c.String(), 15 }) 16 .PrimaryKey(t => t.Id); 17 18 } 19 //版本降級 20 public override void Down() 21 { 22 //刪除表 23 DropTable("dbo.V_MyEntity"); 24 } 25 }
咱們固然不想要建立表啦,咱們要建立視圖,那就把這個文件改一改,改爲以下這個樣子(本文最最最重要的環節):
1 //代碼遷移版本:v2 2 public partial class v2 : DbMigration 3 { 4 //版本升級 5 public override void Up() 6 { 7 //建立視圖(本文最最最重要的環節) 8 Sql(@"create view V_MyEntity as select id,name,substring(name,1,1) first_name from MyEntities"); 9 } 10 //版本降級 11 public override void Down() 12 { 13 //刪除視圖 14 Sql("drop view V_MyEntity"); 15 } 16 }
改好以後,再執行數據遷移的最後一個命令,更新數據庫:
Update-Database
這個時候,咱們發現數據庫沒有多出一個 V_MyEntity 的表,而是多出了一個 V_MyEntity 的視圖,目的達到。
最後咱們再運行程序,程序也沒有再報錯了,目的妥妥的達到。
8、在【來自數據庫的 Code First】中開啓數據遷移
先要複習幾個問題:
1) 用【空 Code First 模型】建立的數據庫,默認就是開啓數據遷移的。
2) 用【來自數據庫的 Code First】這種方式,其數據庫默認是關閉數據遷移的。
3) 判斷一個數據庫有沒有啓用數據遷移,辦法是:檢查有沒有 __MigrationHistory 表。
也許雖然你比較欣賞數據庫中開啓了數據遷移的這種模式,可是無奈你已經有一個數據庫啦,數據庫裏80多張表也已經建立好啦,那咱們固然要用【來自數據庫的 Code First】,來幫助咱們自動生成這80多個實體類啦。
但是,對於一個已經存在的數據庫,咱們能開啓這個數據庫的數據遷移模式嗎?答案固然也是能夠的,具體操做以下。
好比咱們導入了一個Student表:
1 //我進行了刪減 2 public partial class Student 3 { 4 public int Id { get; set; } 5 public string Name { get; set; } 6 }
1 //我進行了刪減 2 public partial class Model1 : DbContext 3 { 4 public Model1() : base("name=Model1") { } 5 public virtual DbSet<Student> Student { get; set; } 6 }
而後二話不說,直接在項目中開啓代碼遷移(請注意這只是在項目中開啓代碼遷移,並無在數據庫中開啓代碼遷移):
Enable-Migrations
而後再二話不說,建立一個遷移版本:
Add-Migration v1
添加了遷移版本以後,會自動產生一個版本文件,內容以下(中文註釋是我寫的,不是自動生成的,略也是我刪減的):
1 //數據遷移版本名稱:v1 2 public partial class v1 : DbMigration 3 { 4 //版本升級 5 public override void Up() 6 { 7 //建表 8 CreateTable("dbo.Student",..略..); 9 } 10 //版本降級 11 public override void Down() 12 { 13 //刪表 14 DropTable("dbo.Student"); 15 } 16 }
而後再二話不說,執行更新數據庫的操做(這等因而一口氣把數據遷移的三個命令挨個打了個遍):
Update-Database
而後就報錯了:
錯誤提示數據庫已經有Student這個表了,這個問題怎麼解決呢?很好解決,辦法就是找到以前那個版本文件,把版本升級Up()方法中的代碼所有註釋掉,以下:
1 //數據遷移版本名稱:v1 2 public partial class v1 : DbMigration 3 { 4 //版本升級 5 public override void Up() 6 { 7 //建表 8 //CreateTable("dbo.Student",..略..); //註釋掉這些代碼是重要環節 9 } 10 //版本降級 11 public override void Down() 12 { 13 //刪表 14 //DropTable("dbo.Student"); //註釋掉這些代碼是重要環節 15 } 16 }
註釋掉以後,再執行 Update-Database ,此次就不報錯了。
此時,數據庫中多了一個 __MigrationHistory 表,這表示數據庫的數據遷移功能已開啓,目的已達到:
最後,爲了未來考慮,最好將剛剛註釋的代碼又取消註釋,這徹底不影響你以後的使用。
總結:
1) EF 中只有 Code First 模式支持 new Model1(這裏寫鏈接字符串),也就是說能夠對其進行加密,這使得 Code First 成爲大部分狀況下的最佳選擇。
2) Code First 中,當模型與實際表結構不一致時,程序會報錯,咱們有兩種方式來使他們一致,一是手動修改模型代碼和表結構,二是使用數據遷移功能。
這兩種方式,不是你想選哪種就選哪種的,取決於一個關鍵因素:數據庫是否開啓了數據遷移
3) 數據遷移的開啓,分兩個層面,一是項目的數據遷移開啓,二是數據庫的數據遷移開啓,前者並不怎麼重要,後者決定了你須要手動仍是自動來使模型與實際表結構一致。
項目中若是包含Migrations文件夾而且裏面有相關的類,說明項目已開啓了數據遷移。
數據庫中若是有 __MigrationHistory 表,說明數據庫已開啓了數據遷移。
4) 刪除數據庫中的 __MigrationHistory 表,也就關閉了數據庫中數據遷移功能,關閉數據遷移的目的是:
你可能並不熟悉數據遷移是個什麼鬼,那不如就讓它見鬼去吧。關閉它,會讓你進入你熟悉世界,經過手動改寫實體類,手動改數據庫表結構,來使模型與實際表結構一致,挺好!
5) 個人建議是:不要關閉數據遷移,仍是能夠嘗試學習一下數據遷移模式的。在程序包管理器控制檯下,使用如下三個命令就可使用數據遷移模式了:
*) 在項目中啓用數據遷移 Enable-Migrations
*) 建立遷移版本 Add-Migration 版本名稱
*) 更新數據庫 Update-Database
6) 在數據庫關閉數據遷移的狀況下,建立視圖的方式與建立表同樣,寫好實體類 V_ABC 以後,再到數據庫裏建立 V_ABC 的視圖就好了。
7) 在數據庫開啓數據遷移的狀況下,建立視圖的方式是先寫好 V_ABC 實體類,再添加遷移版本,而後在遷移版本的代碼中註釋建立表 V_ABC 的代碼,改成建立視圖 V_ABC 的代碼,最後執行數據遷移中的更新數據庫命令便可。
8) 巨坑:當你在執行數據遷移的三個命令時,它們永遠是在 Web.config/App.config 中讀取鏈接字符串!別誤覺得是 new Model1(這裏的字符串) 啦。