迁移提供一组允许以下操作的工具:
- 创建可用于 EF 模型的初始数据库
- 生成迁移以跟踪对 EF 模型所做的更改
- 使数据库随时掌握这些更改
包含以下主题:
生成初始模型和数据库
对于此演练,我们将使用规范的“博客”和“帖子”模型。
- 创建新的 MigrationsDemo 控制台应用程序
-
将最新版本的 EntityFramework NuGet 包添加到项目中
- “工具”–>“库包管理器”–>“包管理器控制台”
- 运行 Install-Package EntityFramework 命令
- 此代码定义了构成域模型的单个“博客”类和 EF Code First 上下文 BlogContext 类
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}
- 更新 Program.cs 文件,其代码如下所示。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsDemo
{
class Program
{
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();
}
}
}
-
运行应用程序,随即会创建 MigrationsCodeDemo.BlogContext 数据库。
启用迁移
现可对模型进行更多更改。
- 将 Url 属性引入“博客”类。
public string Url { get; set; }
http://go.microsoft.com/fwlink/?LinkId=238269)”。
第一步是启用上下文迁移。
-
在包管理器控制台中运行 Enable-Migrations 命令
此新文件夹包含两个文件:
-
由于项目中只有一个 Code First 上下文,因此 Enable-Migrations 已自动填充此配置适用的上下文类型。
-
如果尚未创建数据库,则不会将此 InitialCreate 迁移添加到项目中。相反,第一次调用 Add-Migration 时,会将创建这些表的代码构建到新的迁移中。
针对同一数据库的多个模型
这是因为每个数据库的单个 __MigrationsHistory 表无法识别哪些项属于哪个模型。
默认情况下,此属性设置为上下文的完全限定名称。
生成和运行迁移
Code First 迁移具有两个需要用户了解的主要命令。
- Add-Migration 将基于自上次迁移创建以来对模型所做的更改来构建下一次迁移
- Update-Database 将对数据库应用任意挂起的迁移
Add-Migration 命令可为这些迁移命名,仅需调用 AddBlogUrl。
- 在包管理器控制台中运行 Add-Migration AddBlogUrl 命令
- 迁移文件名以时间戳作为前缀,这样有助于排序
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 将此迁移应用到数据库。
- 在包管理器控制台运行 Update-Database 命令
- 迁移会发现需应用 AddBlogUrl 迁移,并运行它。
MigrationsDemo.BlogContext 数据库现已更新,其中包含“博客”表中的 Url 列。
自定义迁移
现在我们来看看如何编辑默认生成的代码。
- 现在可对模型进行更多更改,我们将新的 Rating 属性添加到“博客”类
public int Rating { get; set; }
- 同时添加一个新的“帖子”类
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; }
}
- 此外,再将“帖子”集合添加到“博客”类,以形成“博客”和“帖子”之间的另一层关系
public virtual List<Post> Posts { get; set; }
我们将调用此迁移 AddPostClass。
- 在包管理器控制台中运行 Add-Migration AddPostClass 命令。
Code First 迁移出色的构建了这些更改,但我们可能还需要做出一些更改:
- 首先,将唯一索引添加到 Posts.Title 列(添加在以下代码的 22 和 29 行)。
- (可以看到以下代码的第 24 行指定的默认值)
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" });
DropIndex("dbo.Posts", new[] { "BlogId" });
DropForeignKey("dbo.Posts", "BlogId", "dbo.Blogs");
DropColumn("dbo.Blogs", "Rating");
DropTable("dbo.Posts");
}
}
}
这次指定 –Verbose 标志,以便可以看到 Code First 迁移正在运行的 SQL。
- 在包管理器控制台中运行 Update-Database –Verbose 命令。
数据移动/自定义 SQL
目前还没有对数据移动的原生支持,但我们可以在脚本中的任何位置运行一些任意 SQL 命令。
- 稍后,我们将使用“内容”列开头的一些文本预填充现有帖子的“摘要”。
public string Abstract { get; set; }
使用 Add-Migration 命令使 Code First 迁移提供对迁移的最佳猜测
- 在包管理器控制台中运行 Add-Migration AddPostAbstract 命令。
- (添加在以下代码的第 12 行中)
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");
}
}
}
我们将指定 –Verbose 标志,以便可以看到针对数据库运行的 SQL。
- 在包管理器控制台中运行 Update-Database –Verbose 命令。
迁移到特定版本(包括降级)
到目前为止,我们一直在升级到最新迁移,但用户有时可能希望升级/降级到特定迁移。
此时可使用 –TargetMigration 切换为降级到此迁移。
- 在包管理器控制台中运行 Update-Database –TargetMigration:AddBlogUrl 命令。
此命令将为 AddBlogAbstract 和 AddPostClass 迁移运行 Down 脚本。
如果想要一直回退到空数据库,可使用 Update-Database –TargetMigration: $InitialDatabase 命令。
获取 SQL 脚本
但是,如果想将这些更改推送到测试服务器以及最终的产品,则可能需要一个可以传递给 DBA 的 SQL 脚本。
- 如果未指定目标迁移,迁移将使用最新的迁移作为目标。如果未指定源迁移,迁移将使用数据库的当前状态。
- 在包管理器控制台中运行 Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration:AddPostAbstract 命令
生成脚本后,将在 Visual Studio 中打开,以供查看或保存。
生成幂等脚本
生成的脚本包括检查 __MigrationsHistory 表的逻辑,并且仅应用以前未应用的更改。
应用程序启动时自动升级(MigrateDatabaseToLatestVersion 初始值设定项)
第一次在应用程序进程中使用上下文时,会运行此逻辑 (AppDomain)。
请注意,还需要为 System.Data.Entity 命名空间(第 5 行)添加 using 语句。
创建此初始值设定项的实例时,需要指定上下文类型 (BlogContext) 和迁移配置(配置)- 迁移配置是启用迁移时添加到“迁移”文件夹的类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
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();
}
}
}
现在,每当应用程序运行时,它首先会检查其目标数据库是否为最新,如果不是,则会应用各种挂起的迁移。
https://docs.microsoft.com/zh-cn/ef/ef6/modeling/code-first/migrations/index