Entity Framework Code First (八)遷移 Migrations

建立初始模型和數據庫數據庫

  在開始使用遷移(Migrations)以前,咱們須要一個 Project 和一個 Code First Model, 對於本文將使用典型的 BlogPost 模型服務器

  • 建立一個新的控制檯應用程序 MigrationsDemo;
  • 添加最新的 EntityFramework 到項目
    • Tools –> Library Package Manager –> Package Manager Console;
    • 運行命令 Install-Package EntityFramework  
  •   建立 Blog.cs DbContext 的派生類 BlogContext.cs
public class Blog { public int BlogId { get; set; } public string Name { get; set; } }
public class BlogContext : DbContext { public DbSet<Blog> Blogs { get; set; } }

  更改 Program.cs 以調用ide

複製代碼
static void Main(string[] args) { using (var db = new BlogContext()) { db.Blogs.Add(new Blog { Name = "Another Blog" }); db.SaveChanges(); foreach (var blog in db.Blogs) { Console.WriteLine(blog.Name); } } Console.WriteLine("Press any key to exit..."); Console.ReadKey(); }
複製代碼

  運行查看結果函數

  發現如上錯誤"CREATE DATABSE permission denied in databse 'master'"post

  咱們在 BlogContext 上的無參構造函數上添加診斷代碼並設置調試斷點測試

System.Diagnostics.Debug.Write(Database.Connection.ConnectionString);

  再次運行ui

  咱們注意到 Data Scource 居然是 .\\SQLEXPRESS 而不是咱們想要的 localDB , 這是由於:spa

  • 若是咱們安裝了 SQL Express,那麼 database 將會安裝在 local SQL Express instance,不然 Code First 纔將嘗試使用 localDB;
  • SQL Express 老是具備優先權,只要安裝了它

  知道了緣由咱們就好解決了:調試

  • 若是想繼續使用 SQL Express,那麼就配置相應地權限,請參考 http://odetocode.com/Blogs/scott/archive/2012/08/14/a-troubleshooting-guide-for-entity-framework-connections-amp-migrations.aspx;
  • 若是想改用 localDB, 只需在.config 配置便可(放在 configSections 節點後面)
  <connectionStrings> <add name="BlogContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=BlogContext;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/> </connectionStrings>

  再次運行就好了,讓咱們看一下後臺生成的數據庫code

 

啓用遷移

  咱們對模型 Blog 作一些更改:增長一個 Url 屬性 

public string Url { get; set; }

  咱們此時再次運行程序,發現以下錯誤

  'InvalidOperationException' was unhandled. The model backing the 'BlogContext' context has changed since the database was created. Consider using Code First Migrations to update the database ( http://go.microsoft.com/fwlink/?LinkId=238269)

  正如錯誤消息提示的那樣,是時候使用 Code First Migrations,第一步是運行以下的命令:

  • 在 Package Manager Console 下運行命令 Enable-Migrations

 

  這個命令將在項目下建立文件夾 Migrations 

  • The Configuration class 這個類容許你去配置如何遷移,對於本文將使用默認的配置(在本文中由於只有一個 Context, Enable-Migrations 將自動對 context type 做出適配);
  • An InitialCreate migration (本文爲 201312240822431_InitialCreate.cs)這個遷移之因此存在是由於咱們以前用 Code First 建立了數據庫, 在啓用遷移前,scaffolded migration 裏的代碼表示在數據庫中已經建立的對象,本文中即爲表 Blog (列 BlogIdName). 文件名包含一個 timestamp 以便排序(若是以前數據庫沒有被建立,那麼 InitialCreate migration 將不會被建立,相反,當咱們第一次調用 Add-Migration 的時候全部表都將歸集到一個新的 migration 中)

 

多個實體鎖定同一數據庫

  當使用 EF6 以前的版本時,只會有一個 Code First Model 被用來生成/管理數據庫的 Schema, 這將致使每一個數據庫只會有一張 __MigrationsHistory 表,從而沒法辨別實體與模型的對應關係。

   從 EF6 開始,Configuration 類將會包含一個 ContextKey 屬性,它將做爲每個 Code First Model 的惟一標識符, __MigrationsHistory 表中一個相應地的列容許來自多個模型(multiple models)的實體共享表(entries),默認狀況下這個屬性被設置成 context 的徹底限定名。

 

生成、運行遷移

  Code First Migrations 有兩個你須要熟悉的命令:

  •  Add-Migration 將 scaffold 建立下一次基於上一次遷移以來的更改的遷移;
  • Update-Databse 將任何掛起的遷移應用到數據庫

  咱們須要腳手架(scaffold 直譯)一個遷移,以上面的 Url 屬性爲例,命令 Add-Migration 容許咱們對遷移命名,咱們姑且稱之爲 AddBlogUrl

  • Package Manager Console 中運行命令 Add-Migration AddBlogUrl;
  • 一個新的遷移(名稱包含 timestamp 前綴)在目錄 Migrations 中建立成功

 

複製代碼
namespace MigrationsDemo.Migrations { using System; using System.Data.Entity.Migrations; public partial class AddBlogUrl : DbMigration { public override void Up() { AddColumn("dbo.Blogs", "Url", c => c.String()); } public override void Down() { DropColumn("dbo.Blogs", "Url"); } } }
複製代碼

  咱們如今能夠對這個遷移進行編輯或者增長,但彷佛看起來還不錯,那咱們就直接用 Update-Database 來應用到數據庫吧

  • 在 Package Manager Console 中運行命令 Update-Database ;
  • AddBlogUrl 遷移將會被應用到數據庫(表 Blogs 增長一列 Url

 

 

定製化遷移

  到目前爲止咱們生成並運行了一個遷移,可是沒有對遷移作任何更改,下面咱們將嘗試作一些更改:在類 Bolg 上增長一屬性 Rating

public int Rating { get; set; }

  新建 Post

複製代碼
public class Post { public int PostId { get; set; } [MaxLength(200)] public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } public Blog Blog { get; set; } }
複製代碼

  在 Blog 中添加 Post 的集合

public virtual ICollection<Post> Posts { get; set; }

  在 Package Manager Console 中運行命令 Add-Migration AddPostClass

  生成的遷移以下

View Code

  接下來咱們對遷移作些更改:

  • 在 Posts.Title 列上增長惟一索引;
  • 使 Blogs.Rating 列非空,對於表中已經存在的數據,新列都會被賦值成 CLR 的默認數據類型(如 Rating 是整型,故默認值爲0),可是咱們想指定默認值爲3,這樣存在的記錄將會有一個合理的評分。

  更改後的代碼以下

複製代碼
namespace MigrationsDemo.Migrations { using System; using System.Data.Entity.Migrations; public partial class AddPostClass : DbMigration { public override void Up() { CreateTable( "dbo.Posts", c => new { PostId = c.Int(nullable: false, identity: true), Title = c.String(maxLength: 200), Content = c.String(), BlogId = c.Int(nullable: false), }) .PrimaryKey(t => t.PostId) .ForeignKey("dbo.Blogs", t => t.BlogId, cascadeDelete: true) .Index(t => t.BlogId) .Index(p => p.Title, unique: true); AddColumn("dbo.Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3)); } public override void Down() { DropIndex("dbo.Posts", new[] { "Title" }); DropForeignKey("dbo.Posts", "BlogId", "dbo.Blogs"); DropIndex("dbo.Posts", new[] { "BlogId" }); DropColumn("dbo.Blogs", "Rating"); DropTable("dbo.Posts"); } } }
複製代碼

  在 Package Manager Console 中運行命令 Update-Database –Verbose

 

數據移動 / 定製SQL 

  迄今爲止,遷移都沒有更改或移動數據,如今讓咱們看一下須要移動數據的例子。雖然沒有對數據移動的原生支持,可是咱們能夠隨意運行 SQL 腳本。

  讓咱們在 Post 中增長一個屬性 Abstract, 稍後咱們使用列 Content 的開頭來填充此列(數據庫已有記錄) 

public string Abstract { get; set; }
  • 在 Package Manager Console 中運行命令 Add-Migration AddPostAbstract ;
  • 生成的遷移很是好,可是咱們想使用 Content 的前 100 個字符來預填充 Abstract 列,咱們可對遷移作以下更改
複製代碼
namespace MigrationsDemo.Migrations { using System; using System.Data.Entity.Migrations; public partial class AddPostAbstract : DbMigration { public override void Up() { AddColumn("dbo.Posts", "Abstract", c => c.String()); Sql("UPDATE dbo.Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL"); } public override void Down() { DropColumn("dbo.Posts", "Abstract"); } } }
複製代碼

  在 Package Manager Console 中運行命令 Update-Database –Verbose

 

遷移至指定版本(包括後退)

  迄今爲止,咱們老是升級至最新遷移,然而某些時候咱們須要升級/降級至指定版本,例如咱們想遷移數據庫至運行 AddBlogUrl 遷移以後的狀態,此時咱們就可使用 –TargetMigration 來降級到這個版本

  在 Package Manager Console 中運行命令 Update-Database –TargetMigration: AddBlogUrl 

  這個命令將會運行 AddBlogAbstract and AddPostClass Down 命令

  若是你想回滾一切至空數據庫,可使用命令 Update-Database –TargetMigration: $InitialDatabase

 

 

獲得SQL腳本

  若是其它開發人員也但願在他們本身的機器上擁有這些更改,他們只需在咱們 check in 代碼至 source control 的時候作一次同步便可,一旦他們擁有了這些遷移,只需運行命令 Update-Database 就能夠把這些更改應用於本地。可是若是咱們想把這些更改推送至測試服務器或生產服務器,咱們也許須要一份 SQL 腳本提供給 DBA

  • 在運行 Update-Database 的時候指定 -Specify 標記,咱們就可以使得這些更改被寫入一個腳本中而不是被應用,咱們同時也會爲此腳本指定源遷移和目標遷移,例如咱們但願產生的腳本是從一個空數據庫($InitialDatabase)到最新的版本(AddPostAbstract 遷移);(注意:若是你沒有指定目標遷移,那麼遷移將始終更新至最新版本;若是你沒有指定源遷移,那麼遷移將以數據庫目前狀態爲初始)
  • 在 Package Manager Console 中運行命令 Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration: AddPostAbstract

  產生的 SQL 腳本以下

View Code

 

產生冪等腳本(EF6+)

  從 EF6 開始,若是你使用 –SourceMigration $InitialDatabase, 產生的腳本將是冪等的,冪等腳本意味着不管數據庫當前處於什麼版本/狀態,都能升級至最新版本或指定版本(指定 TargetMigration),生成的腳本包括檢查表 __MigrationsHistory 的邏輯以及只更新以前從未更新的

 

在應用程序啓動時自動升級(MigrateDatabaseToLatestVersion初始化器)

  當你發佈部署應用程序的時候,可能但願當程序啓動的時候它自動更新數據庫(更新應用任何未更新的遷移),你能夠經過註冊 MigrateDatabaseToLatestVersion 數據庫初始化器來實現這一點,數據庫初始化器只包含一些邏輯檢查用於確保數據庫被正確設置,這個邏輯檢查將會在AppDomaincontext 第一次被使用的時候執行。

  當咱們建立一個初始化器的實例時,須要指定 context typeBlogContext)以及 migrations configuration Configuration)- 這個遷移配置類是在咱們啓用遷移時生成的 Migrations 目錄下增長的

複製代碼
using System; using System.Collections.Generic; using System.Data.Entity; using System.Linq; using System.Text; using System.Threading.Tasks; using MigrationsDemo.Migrations; namespace MigrationsDemo { class Program { static void Main(string[] args) { Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>()); using (var db = new BlogContext()) { db.Blogs.Add(new Blog { Name = "Another Blog " }); db.SaveChanges(); foreach (var blog in db.Blogs) { Console.WriteLine(blog.Name); } } Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } } }
複製代碼

原文:http://msdn.microsoft.com/en-us/data/jj591621

相關文章
相關標籤/搜索