使用CodeFirst创建并更新数据库
本文主要介紹如何使用CodeFirst模式來(lái)新建并更新數(shù)據(jù)庫(kù)
在使用Entity Framwork的三種方式(ModelFist、DBFirst、CodeFirst)中,CodeFirst方式書(shū)寫(xiě)的代碼最為干凈。
至于CodeFist方式的詳細(xì)優(yōu)缺點(diǎn)請(qǐng)各位讀者自行搜索,這里不多贅述。
1. 使用CodeFirst方式創(chuàng)建數(shù)據(jù)庫(kù)
我們新建一個(gè)控制臺(tái)項(xiàng)目,項(xiàng)目中添加兩個(gè)Model:Author和Blog以及DbContext。 DbContext的添加方式如下:
項(xiàng)目上右鍵->添加->新建項(xiàng)->ADO.NET Entity Data Model->Empty Code First model
項(xiàng)目代碼如下:
1 //默認(rèn)生成的數(shù)據(jù)表名為類(lèi)名+字母s,這里使用TableAttribute來(lái)指定數(shù)據(jù)表名為T(mén)_Authors 2 [Table("T_Authors")] 3 public class Author 4 { 5 public int Id { set; get; } 6 public string Name { set; get; } 7 /* 8 此處定義了Blog類(lèi)型的屬性,所以要確保Blog類(lèi)中至少要有一個(gè)表示主鍵的字段,即public int Id { set; get; }。否則在生成數(shù)據(jù)表時(shí)會(huì)報(bào)錯(cuò):"EntityType 'Blog' has no key defined. Define the key for this EntityType. 9 Blogs: EntityType: EntitySet 'Blogs' is based on type 'Blog' that has no keys defined." 10 */ 11 public virtual ICollection<Blog> Blogs { set; get; } 12 } 13 14 [Table("T_Blogs")] 15 public class Blog 16 { 17 public int Id { set; get; } 18 public string Title { set; get; } 19 public DateTime Time { set; get; } 20 public int AuthorId { set; get; } 21 public virtual Author Author { set; get; } 22 } 23 24 public class MyDbContext: DbContext 25 { 26 public MyDbContext() 27 : base("name=MyDbContext") 28 { 29 } 30 //DbContext會(huì)根據(jù)配置文件中connectionStrings指定的數(shù)據(jù)庫(kù)名稱(chēng)來(lái)建立數(shù)據(jù)庫(kù) 31 //DbContext根據(jù)DbSet屬性的類(lèi)型來(lái)創(chuàng)建數(shù)據(jù)表,這里指定了Author類(lèi)型的屬性,所以會(huì)生成T_Authors數(shù)據(jù)表 32 public virtual DbSet<Author> Authors { set; get; } 33 }
?
CodeFirst方式會(huì)根據(jù)配置文件中的配置生成數(shù)據(jù)庫(kù),這里小編使用的是MYSQL數(shù)據(jù)庫(kù),配置文件如下:
1 <!--EF for MYSQL--> 2 <entityFramework codeConfigurationType="MySql.Data.Entity.MySqlEFConfiguration, MySql.Data.Entity.EF6"> 3 <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" /> 4 <providers> 5 <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" /> 6 </providers> 7 </entityFramework> 8 <connectionStrings> 9 <add name="MyDbContext" connectionString="server=localhost;port=3306;database=EF;uid=root;password=root" providerName="System.Data.MySqlClient" /> 10 </connectionStrings>PS:小編使用的是EF6和MYSQL數(shù)據(jù)庫(kù),所以要在項(xiàng)目中添加對(duì)Mysql.Data.Entity.EF6以及EntityFrameword 6.0的引用。
到此,我們已經(jīng)完成生成數(shù)據(jù)庫(kù)的工作,接下來(lái)在Main方法中寫(xiě)兩行代碼:
1 using (var db = new MyDbContext()) 2 { 3 db.Authors.Add(new Author() { Name = "xfh" }); 4 db.SaveChanges(); 5 }?運(yùn)行程序,我們會(huì)發(fā)現(xiàn)EF已經(jīng)為我們建立了數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)名稱(chēng)為EF(在配置文件中指定)以及數(shù)據(jù)表T_Authors。
雖然我們沒(méi)有為DbContext添加Blog類(lèi)型的屬性,但依然創(chuàng)建了數(shù)據(jù)表T_Blogs,這是因?yàn)樵贏uthor類(lèi)中定義了Blog類(lèi)型的屬性,若我們注釋掉
?public virtual ICollection<Blog> Blogs { set; get; }?,刪除數(shù)據(jù)庫(kù)重新生成就會(huì)發(fā)現(xiàn)不在生成T_Blogs表。在生成T_Authors表的同時(shí),會(huì)生成一張名為_(kāi)migrationhistory表,這張表用于記錄我們對(duì)于數(shù)據(jù)庫(kù)的更新日志,表中的MigrationId字段的值是我們每次執(zhí)行Migration時(shí)所生成的文件名,根據(jù)該字段的值我們可以使用命令?Update-Database -TargetMigration:MigrationIdValue?來(lái)將數(shù)據(jù)庫(kù)恢復(fù)到MigrationIdValue所對(duì)應(yīng)的Migration狀態(tài),和Git版本控制有點(diǎn)兒像,但這里若將數(shù)據(jù)庫(kù)回滾到以前的版本會(huì)導(dǎo)致數(shù)據(jù)的丟失,并且_migrationhistory表也會(huì)刪除所記錄的當(dāng)前Migration信息。
2. 更新數(shù)據(jù)庫(kù)(Code Fist Migration)
現(xiàn)在,我們給Author類(lèi)增加字屬性Email,代碼如下:
[Table("T_Authors")]public class Author{public int Id { set; get; }public string Name { set; get; }public string Email { set; get; }public virtual ICollection<Blog> Blogs { set; get; }}此時(shí),再次運(yùn)行該應(yīng)用程序,則將拋出異常
An unhandled exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll The model backing the 'MyDbContext' context has changed since the database was created. Consider using Code First Migrations toupdate the database (http://go.microsoft.com/fwlink/?LinkId=238269).
異常信息中提示我們數(shù)據(jù)庫(kù)創(chuàng)建之后model發(fā)生了變化,所以我們需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行更新使二者保持一致才能運(yùn)行程序。
2.1 啟用遷移
對(duì)于首次遷移需要啟用遷移,具體方法是在Packge Manager Console中輸入Enable-Migrations命令即可。
命令運(yùn)行完畢之后我們會(huì)看到命令行中的提示信息
這里我們只是啟用了遷移,但不是自動(dòng)遷移。從提示信息中我們可以看到若要啟用自動(dòng)遷移則要?jiǎng)h除Migrations文件夾并在Packge Manager Console中輸入
Enable-Migrations –EnableAutomaticMigrations命令或者在Migrations文件夾的Configuration.cs文件中設(shè)置AutomaticMigrationsEnabled屬性為true即可。
Enable-Migrations命令運(yùn)行完畢之后會(huì)在項(xiàng)目中生成如下文件:
Configuration文件?我們可以在此文件中針對(duì)上下文配置遷移行為。
InitialCreate文件 ??因?yàn)槲覀兪孪茸?Code First 自動(dòng)創(chuàng)建了一個(gè)數(shù)據(jù)庫(kù),這個(gè)遷移文件中的代碼表示數(shù)據(jù)庫(kù)中已創(chuàng)建的對(duì)象。該文件文件名包含時(shí)間戳,這對(duì)于排序十分有幫助。如果尚未創(chuàng)建數(shù)據(jù)庫(kù),則不會(huì)將此 InitialCreate 遷移添加到項(xiàng)目中。而是,首次調(diào)用 Add-Migration 時(shí),用于創(chuàng)建這些表的代碼將為新遷移搭建基架。
2.2 更新數(shù)據(jù)庫(kù)
啟用遷移之后,在Packge Manager Console中繼續(xù)輸入U(xiǎn)pdate-Database命令來(lái)更新數(shù)據(jù)庫(kù),但會(huì)發(fā)現(xiàn)更新失敗。
通過(guò)上面的提示信息我們可以知道,要想更新數(shù)據(jù)庫(kù)需要啟用自動(dòng)遷移或者使用Add-Migration命令來(lái)創(chuàng)建遷移文件。
2.2.2 基于代碼的遷移
我們?cè)赑ackge Manager Console中輸入命令A(yù)dd-Migration AddEmail,命令運(yùn)行完畢后我們會(huì)發(fā)現(xiàn)Migrations文件夾下已經(jīng)創(chuàng)建了_AddEmail文件,文件內(nèi)容如下:
1 public partial class AddEmail : DbMigration 2 { 3 public override void Up() 4 { 5 //注意,這里數(shù)據(jù)表的名稱(chēng)是dbo.T_Blogs和dbo.T_Authors而不是我們指定的 6 //T_Blogs和T_Authors,我們可以把數(shù)據(jù)表名稱(chēng)改為T(mén)_Blogs和T_Authors 7 //數(shù)據(jù)表以dbo開(kāi)頭貌似是SQL SERVER中的命名方式,這里小編使用的是MYSQL 8 CreateTable( 9 "dbo.T_Blogs", 10 c => new 11 { 12 Id = c.Int(nullable: false, identity: true), 13 Author_Id = c.Int(), 14 }) 15 .PrimaryKey(t => t.Id) 16 .ForeignKey("dbo.T_Authors", t => t.Author_Id) 17 .Index(t => t.Author_Id); 18 19 AddColumn("dbo.T_Authors", "Email", c => c.String(unicode: false)); 20 } 21 22 public override void Down() 23 { 24 DropForeignKey("dbo.T_Blogs", "Author_Id", "dbo.T_Authors"); 25 DropIndex("dbo.T_Blogs", new[] { "Author_Id" }); 26 DropColumn("dbo.T_Authors", "Email"); 27 DropTable("dbo.T_Blogs"); 28 } 29 } 30?
我們可以看到Up方法中調(diào)用了AddColumn方法來(lái)向數(shù)據(jù)庫(kù)中添加Email字段。這時(shí)我們?cè)赑ackge Manager Console中輸入命令Update-Database命令(也可以使用Update-Database -Verbose命令,該命令可以使我們看到SQL語(yǔ)句的執(zhí)行過(guò)程,注意-Verbose和-Database之間有個(gè)空格)并運(yùn)行,可以看到命令成功執(zhí)行,然后到數(shù)據(jù)庫(kù)中查看數(shù)據(jù)表T_Authors發(fā)現(xiàn)表中已經(jīng)添加字段Email,同時(shí)數(shù)據(jù)表__migrationhistory中對(duì)于我們的此次更新進(jìn)行了記錄。
對(duì)于上述代碼我們可以進(jìn)行簡(jiǎn)化:
public partial class AddEmail : DbMigration {public override void Up(){AddColumn("T_Authors", "Email", c => c.String(unicode: false));}public override void Down(){DropColumn("T_Authors", "Email");} }我們也可以直接通過(guò)創(chuàng)建遷移文件來(lái)更新數(shù)據(jù)庫(kù)而不修改Model。如:
通過(guò)命令A(yù)dd-Migration addAge來(lái)創(chuàng)建一個(gè)新的遷移文件,代碼如下:
public partial class AddAge : DbMigration {public override void Up(){AddColumn("T_Authors", "Age", c => c.Int(nullable: false,defaultValue:18));}public override void Down(){DropColumn("T_Authors", "Age");} }然后運(yùn)行命令Update-Database,我們會(huì)發(fā)現(xiàn)數(shù)據(jù)庫(kù)T_Authors表中創(chuàng)建了字段Age。雖然成功的更新了數(shù)據(jù)庫(kù),但會(huì)導(dǎo)致程序中的Model和數(shù)據(jù)表不匹配。
2.2.2 ?自動(dòng)遷移?
啟用自動(dòng)遷移的方法前文已經(jīng)陳述。在啟用自動(dòng)遷移之后,我們?cè)傩薷腗odel文件,只需執(zhí)行Update-Database命令即可完成對(duì)數(shù)據(jù)庫(kù)的更新。
?
最后再補(bǔ)充一點(diǎn),在創(chuàng)建數(shù)據(jù)庫(kù)之后若修改TableAttribute和ColumnAttribute的值,那么在執(zhí)行程序時(shí)EF會(huì)按照TabelAttribute和ColumnAttribute中指定的值和數(shù)據(jù)庫(kù)進(jìn)行匹配,但數(shù)據(jù)庫(kù)中并不存在我們新指定的數(shù)據(jù)表和字段,這會(huì)導(dǎo)致程序報(bào)錯(cuò)。若我們修改了TableAttribute和ColumnAttribute的值,然后再使用Update-Database命令來(lái)更新數(shù)據(jù)庫(kù),數(shù)據(jù)庫(kù)會(huì)新建一張有TableAttribute指定名稱(chēng)的數(shù)據(jù)表。
參考文章:
自動(dòng)化 Code First 遷移
What is Code-First
Code First 遷移
版權(quán)聲明
本文為作者原創(chuàng),版權(quán)歸作者雪飛鴻所有。 轉(zhuǎn)載必須保留文章的完整性,且在頁(yè)面明顯位置處標(biāo)明原文鏈接。
如有問(wèn)題, 請(qǐng)發(fā)送郵件和作者聯(lián)系。
轉(zhuǎn)載于:https://www.cnblogs.com/Cwj-XFH/p/5613544.html
總結(jié)
以上是生活随笔為你收集整理的使用CodeFirst创建并更新数据库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Squid 启动/停止/重载配置文件 命
- 下一篇: 跟多导出数据库的方法