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

相关文章
相关标签/搜索