使用CodeFirst創建並更新數據庫


本文主要介紹如何使用CodeFirst模式來新建並更新數據庫

在使用Entity Framwork的三種方式(ModelFist、DBFirst、CodeFirst)中,CodeFirst方式書寫的代碼最為干凈。

至於CodeFist方式的詳細優缺點請各位讀者自行搜索,這里不多贅述。

1. 使用CodeFirst方式創建數據庫

我們新建一個控制台項目,項目中添加兩個Model:Author和Blog以及DbContext。 DbContext的添加方式如下:

項目上右鍵->添加->新建項->ADO.NET Entity Data Model->Empty Code First model

項目代碼如下:

 1 //默認生成的數據表名為類名+字母s,這里使用TableAttribute來指定數據表名為T_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類型的屬性,所以要確保Blog類中至少要有一個表示主鍵的字段,即public int Id { set; get; }。
      否則在生成數據表時會報錯:"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會根據配置文件中connectionStrings指定的數據庫名稱來建立數據庫 31 //DbContext根據DbSet屬性的類型來創建數據表,這里指定了Author類型的屬性,所以會生成T_Authors數據表 32 public virtual DbSet<Author> Authors { set; get; } 33 }

 

CodeFirst方式會根據配置文件中的配置生成數據庫,這里小編使用的是MYSQL數據庫,配置文件如下:

 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數據庫,所以要在項目中添加對Mysql.Data.Entity.EF6以及EntityFrameword 6.0的引用。

到此,我們已經完成生成數據庫的工作,接下來在Main方法中寫兩行代碼:

1 using (var db = new MyDbContext())
2 {
3     db.Authors.Add(new Author() { Name = "xfh" });
4     db.SaveChanges();
5 }

 運行程序,我們會發現EF已經為我們建立了數據庫,數據庫名稱為EF(在配置文件中指定)以及數據表T_Authors。

雖然我們沒有為DbContext添加Blog類型的屬性,但依然創建了數據表T_Blogs,這是因為在Author類中定義了Blog類型的屬性,若我們注釋掉

 public virtual ICollection<Blog> Blogs { set; get; } ,刪除數據庫重新生成就會發現不在生成T_Blogs表。在生成T_Authors表的同時,會生成一張名為_migrationhistory表,這張表用於記錄我們對於數據庫的更新日志,表中的MigrationId字段的值是我們每次執行Migration時所生成的文件名,根據該字段的值我們可以使用命令 Update-Database -TargetMigration:MigrationIdValue 來將數據庫恢復到MigrationIdValue所對應的Migration狀態,和Git版本控制有點兒像,但這里若將數據庫回滾到以前的版本會導致數據的丟失,並且_migrationhistory表也會刪除所記錄的當前Migration信息。

2. 更新數據庫(Code Fist Migration)

現在,我們給Author類增加字屬性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; }
    }

此時,再次運行該應用程序,則將拋出異常

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 to 
update the database (http://go.microsoft.com/fwlink/?LinkId=238269).

異常信息中提示我們數據庫創建之后model發生了變化,所以我們需要對數據庫進行更新使二者保持一致才能運行程序。

2.1 啟用遷移

對於首次遷移需要啟用遷移,具體方法是在Packge Manager Console中輸入Enable-Migrations命令即可。


命令運行完畢之后我們會看到命令行中的提示信息

這里我們只是啟用了遷移,但不是自動遷移。從提示信息中我們可以看到若要啟用自動遷移則要刪除Migrations文件夾並在Packge Manager Console中輸入

Enable-Migrations –EnableAutomaticMigrations命令或者在Migrations文件夾的Configuration.cs文件中設置AutomaticMigrationsEnabled屬性為true即可。

Enable-Migrations命令運行完畢之后會在項目中生成如下文件:

Configuration文件 我們可以在此文件中針對上下文配置遷移行為。

InitialCreate文件   因為我們事先讓 Code First 自動創建了一個數據庫,這個遷移文件中的代碼表示數據庫中已創建的對象。該文件文件名包含時間戳,這對於排序十分有幫助。如果尚未創建數據庫,則不會將此 InitialCreate 遷移添加到項目中。而是,首次調用 Add-Migration 時,用於創建這些表的代碼將為新遷移搭建基架。

2.2 更新數據庫

啟用遷移之后,在Packge Manager Console中繼續輸入Update-Database命令來更新數據庫,但會發現更新失敗。

通過上面的提示信息我們可以知道,要想更新數據庫需要啟用自動遷移或者使用Add-Migration命令來創建遷移文件。

2.2.2 基於代碼的遷移

我們在Packge Manager Console中輸入命令Add-Migration AddEmail,命令運行完畢后我們會發現Migrations文件夾下已經創建了_AddEmail文件,文件內容如下:

 1 public partial class AddEmail : DbMigration
 2 {
 3     public override void Up()
 4     {
 5         //注意,這里數據表的名稱是dbo.T_Blogs和dbo.T_Authors而不是我們指定的
 6         //T_Blogs和T_Authors,我們可以把數據表名稱改為T_Blogs和T_Authors
 7         //數據表以dbo開頭貌似是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方法中調用了AddColumn方法來向數據庫中添加Email字段。這時我們在Packge Manager Console中輸入命令Update-Database命令(也可以使用Update-Database -Verbose命令,該命令可以使我們看到SQL語句的執行過程,注意-Verbose和-Database之間有個空格)並運行,可以看到命令成功執行,然后到數據庫中查看數據表T_Authors發現表中已經添加字段Email,同時數據表__migrationhistory中對於我們的此次更新進行了記錄。

對於上述代碼我們可以進行簡化:

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");
    }
}

我們也可以直接通過創建遷移文件來更新數據庫而不修改Model。如:

通過命令Add-Migration addAge來創建一個新的遷移文件,代碼如下:

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");
    }
}

然后運行命令Update-Database,我們會發現數據庫T_Authors表中創建了字段Age。雖然成功的更新了數據庫,但會導致程序中的Model和數據表不匹配。

2.2.2  自動遷移 

啟用自動遷移的方法前文已經陳述。在啟用自動遷移之后,我們再修改Model文件,只需執行Update-Database命令即可完成對數據庫的更新。

 

最后再補充一點,在創建數據庫之后若修改TableAttribute和ColumnAttribute的值,那么在執行程序時EF會按照TabelAttribute和ColumnAttribute中指定的值和數據庫進行匹配,但數據庫中並不存在我們新指定的數據表和字段,這會導致程序報錯。若我們修改了TableAttribute和ColumnAttribute的值,然后再使用Update-Database命令來更新數據庫,數據庫會新建一張有TableAttribute指定名稱的數據表。

參考文章:

自動化 Code First 遷移

What is Code-First

Code First 遷移

版權聲明

本文為作者原創,版權歸作者雪飛鴻所有。 轉載必須保留文章的完整性,且在頁面明顯位置處標明原文鏈接

如有問題, 請發送郵件和作者聯系。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM