上一回合,我們討論了如何簡單的使用Entity Framework CodeFirst功能。
結尾的時候,我們提出了一個有趣的問題,如果我們的數據實體需要發生變化呢?需要添加多一個Model類呢?修改已有實體中字段呢?我們該怎么辦?該不會是把數據庫刪掉,然后讓程序重新生成吧?很明顯,答案當然不是啦。EF作為微軟推薦的框架之一,沒有這么差勁的。
本節,我們討論一下內容:
1、Migration控制台
2、修改已有實體,添加/刪除 數據庫字段
3、添加新實體模型與數據庫表映射
4、修改實體屬性與數據庫字段名映射
代碼點擊這里下載
1、打開程序包管理器控制台,並啟動Migrations (遷移)
方法如下:點擊工具->庫程序包管理器->程序包管理器控制台
在控制台中輸入“Enable-Migrations” (小技巧:這里支持Tab自動補全),啟動Migrations。
這時候,項目解決方案處會多出一個文件夾“Migrations”以及其下級的文件
“Configuration.cs”文件包含本項目CodeFirst的基本配置,一般情況下我們大可不必修改里面的內容。

public partial class InitialCreate : DbMigration { public override void Up() { CreateTable( "dbo.Blogs", c => new { BlogID = c.Int(nullable: false, identity: true), BlogName = c.String(), }) .PrimaryKey(t => t.BlogID); CreateTable( "dbo.Posts", c => new { PostID = c.Int(nullable: false, identity: true), Title = c.String(), Content = c.String(), BlogID = c.Int(nullable: false), }) .PrimaryKey(t => t.PostID) .ForeignKey("dbo.Blogs", t => t.BlogID, cascadeDelete: true) .Index(t => t.BlogID); } public override void Down() { DropIndex("dbo.Posts", new[] { "BlogID" }); DropForeignKey("dbo.Posts", "BlogID", "dbo.Blogs"); DropTable("dbo.Posts"); DropTable("dbo.Blogs"); } }
“201303241026370_InitialCreate.cs”(命名有系統自動生成)包含的則是初始化語句。大家可以看到里面包含了兩個方法,一個是Up,用於初始化時建立數據庫(表);另外一個則是Down,刪除數據庫(表)的。
我們必須借助Migrations 來對實體類中的修改更新到數據庫的結構當中。
2、如何修改已有實體(實例:在已有model中添加一個字段)
舉個例子(還是來自於MSDN),我們對Blog Model進行修改,添加一個“Url”的字段
class Blog { public int BlogID { get; set; } public string BlogName { get; set; } public string Url { get; set; } }
打開Migration控制台,輸入“Add-Migration AddUrl”,這時我們發現解決方案中多了一個文件,名為:201303241155123_AddUrl
該文件產生的代碼如下:

public partial class AddUrl : DbMigration { public override void Up() { AddColumn("dbo.Blogs", "Url", c => c.String()); } public override void Down() { DropColumn("dbo.Blogs", "Url"); } }
再在Migration控制台輸入:“Update-Database”,更新數據庫結構。
新增的字段就乖乖的新建出來了。
這里解析一下:
(1)、Migration控制台中,“Add-Migration XXX”,其中的XXX為各位讀者自己命名,可以起任意的名字,這里還是建議各位讀者朋友名一個有實意的名字以方便后續的工作。
(2)、生成的新代碼中,它繼承自DbMigration,里面包含了兩個重寫方法,UP和Down,Up中調用了AddColumn方法插入新字段,傳入三個參數,分別是“表名”、“字段名”以及一個用於確定數據類型的lambda表達式;Drow方法則調用DropColumn,傳入“表名”和“字段名”來刪除該字段。有興趣的同學可以對DbMigration進行反編譯,可以看到里面包含的所有方法。
3、新增一個實體(實例:添加一個Type Model和一個User Model)
3.1、添加一個Type實體
class Type { public int TypeID { get; set; } public string TypeName { get; set; } }
修改上下文BlogContext,添加一個Types屬性
class BlogContext:DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Type> Types { get; set; } }
調出Migration控制台,“Add-Migration AddType”——>“Update-Database” ,很好,Type表已經輕松的生成。
3.2、添加一個User Model
各位讀者有沒有發現,之前我們一直新建的實體,實體字段第一位都是XXID(總是int類型),這里我們做一點改動,不要XXID,只要UserName和DisplayName兩個string類型的字段。
添加Model
class User { public string UserName { get; set; } public string DisplayName { get; set; } }
修改上下文
class BlogContext:DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Type> Types { get; set; } public DbSet<User> Users { get; set; } }
我們還像以往一樣,調出Migration控制台,然后執行“Add-Migration AddUser”——>“Update-Database”
聰明的讀者一定會猜得出后果會怎么樣,沒錯,這回報錯了,報錯內容為:沒有找到主鍵
這時,我們需要在User中手動添加一個主鍵的標簽
class User { [System.ComponentModel.DataAnnotations.Key] public string UserName { get; set; } public string DisplayName { get; set; } }
重新調出Migration控制台,“Add-Migration AddUser”——>“Update-Database”
通過,User表已經生成在數據庫中。
這時,我們或許會產生或多或少的暗示——不指定主鍵的時候,EF默認會自動的幫我們指定。
這里,我把Type實體和User實體通過調用Migration生成的兩個Add文件的代碼貼出來。

public partial class AddType : DbMigration { public override void Up() { CreateTable( "dbo.Types", c => new { TypeID = c.Int(nullable: false, identity: true), TypeName = c.String(), }) .PrimaryKey(t => t.TypeID); } public override void Down() { DropTable("dbo.Types"); } }

public partial class AddUser : DbMigration { public override void Up() { CreateTable( "dbo.Users", c => new { UserName = c.String(nullable: false, maxLength: 128), DisplayName = c.String(), }) .PrimaryKey(t => t.UserName); } public override void Down() { DropTable("dbo.Users"); } }
對比一下,答案也許各位讀者已經找到了。沒錯,答案就在重寫的UP中,各位是否發現,沒有指明主鍵的Type實體中,EF會自動的把第一個字段指定為identity(自增)的int型主鍵,當然,前提必須為這個被指定的字段在model中的屬性為int類型。而在User實體中,全部都是string類型的屬性,因為EF無法通過默認的方式找到可以自動添加的主鍵字段,所以Migrants控制台想Add-Migrants時就會發生內容為:找不到主鍵的報錯啦。
這里我需要補充一下,精明的讀者也許發現,通過CodeFirst生成的數據表中,字段的類型也只能確定個大概,並不能進行比較精確的確定,比如通過string類型屬性反向生成的字段,其字段容量大小竟然為“Max”;其實,在“Update-Database”之前,我們可以通過對“Add-Migration”中生成的Add文件(暫時想不出有什么方法形容這些文件)中的UP方法進行細小的微調,然后再執行“Update-Database”,我們就可以生成真正想要字段大小啦。
3、修改已存在的實體,數據庫中對應字段的字段名
有時候,我們已經使用EF CodeFirst對數據庫已經進行了反向的生成,但是,我們又想修改一下數據庫中,與實體屬性對應的某個字段的字段名,這個時候我們應該怎么辦呢?
這里,我們只需要在上下文中override一個方法
class BlogContext:DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Type> Types { get; set; } public DbSet<User> Users { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<User>() .Property(u => u.DisplayName) .HasColumnName("Display_Name"); } }
在Migration中,“Add-Migration”——>“Update-Migration”
好的,O了。
這節我們已經討論完畢了,各位讀者如果有什么更好的建議或者意見,歡迎留言。