Code First 自动迁移
自动迁移让你可以使用 Code First 迁移,而无需在项目中针对你进行的每个更改创建代码文件。 并非所有更改都可以自动进行应用 - 例如,列重命名需要使用基于代码的迁移。
注意
本文假设你了解如何在基本场景中使用 Code First 迁移。 如果你不知道,则需要先阅读 Code First 迁移,再继续。
针对团队环境的建议
可以交错执行自动迁移和基于代码的迁移,但在团队开发场景中不建议这样做。 如果你是使用源代码控制的开发人员团队的一员,则应使用纯自动迁移或纯基于代码的迁移。 鉴于自动迁移的局限性,我们建议在团队环境中使用基于代码的迁移。
生成初始模型和数据库
开始使用迁移之前,需要会用到项目和 Code First 模型。 对于此演练,我们将使用规范的“博客”和“帖子”模型。
- 创建新的 MigrationsAutomaticDemo 控制台应用程序
- 将最新版本的 EntityFramework NuGet 包添加到项目中
- “工具”–>“库包管理器”–>“包管理器控制台”
- 运行 Install-Package EntityFramework 命令
- 添加 Model.cs 文件,其代码如下所示。 此代码定义了构成域模型的单个“博客”类和 EF Code First 上下文 BlogContext 类
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsAutomaticDemo
{
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 MigrationsAutomaticDemo
{
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();
}
}
}
运行应用程序,随即会创建 MigrationsAutomaticCodeDemo.BlogContext 数据库。
启用迁移
现可对模型进行更多更改。
- 将 Url 属性引入“博客”类。
public string Url { get; set; }
如果要再次运行应用程序,则会收到一个 InvalidOperationException,指出“创建数据库后,支持‘BlogContext’上下文的模型已发生变化。请考虑使用 Code First 迁移更新数据库 ( http://go.microsoft.com/fwlink/?LinkId=238269)。
如异常情况所述,可开始使用 Code First 迁移。 由于我们想要使用自动迁移,因此我们将指定 –EnableAutomaticMigrations 开关。
在包管理器控制台中运行 Enable-Migrations –EnableAutomaticMigrations 命令 此命令已向项目添加“Migrations”文件夹。 此新文件夹包含一个文件:
配置类。 此类允许配置迁移对上下文的行为方式。 对于此演练,将只使用默认配置。 由于项目中只有一个 Code First 上下文,因此 Enable-Migrations 已自动填充此配置适用的上下文类型。
第一次自动迁移
Code First 迁移具有两个需要用户了解的主要命令。
- Add-Migration 将基于自上次迁移创建以来对模型所做的更改来构建下一次迁移
- Update-Database 将对数据库应用任意挂起的迁移
我们将避免使用 Add-Migration(除非确实需要),而是专注于让 Code First 迁移自动计算并应用更改。 使用 Update-Database 来获取 Code First 迁移,将模型(新的 Blog.Url 属性)的更改推送到数据库。
- 在包管理器控制台中运行 Update-Database 命令。
MigrationsAutomaticDemo.BlogContext 数据库现已更新,其中包含“Blogs”表中的 URL 列。
第二次自动迁移
让我们进行另一项更改,让 Code First 迁移自动将更改推送到数据库。
- 同时添加一个新的“帖子”类
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; }
现在使用 Update-Database 更新数据库。 这次指定 –Verbose 标志,以便可以看到 Code First 迁移正在运行的 SQL。
- 在包管理器控制台中运行 Update-Database –Verbose 命令。
添加基于代码的迁移
现在,让我们看看可能需要使用基于代码的迁移的内容。
- 为“Blog”类添加 Rating 属性
public int Rating { get; set; }
可只运行 Update-Database 以将这些更改推送到数据库。 但是,我们添加了一个不可为 null 的 Blogs.Rating 列,如果表中存在任何现有数据,它将被分配新列的数据类型的 CLR 默认值(Rating 是整数,因此将为“0”)。 但我们要将默认值指定为 3,以便“博客”表中的现有行以合适的分级开始。 使用 Add-Migration 命令将此更改写入基于代码的迁移,以便对其进行编辑。 Add-Migration 命令可为这些迁移命名,仅需调用 AddBlogRating。
- 在包管理器控制台中运行 Add-Migration AddBlogRating 命令。
- 现在“Migrations”文件夹中已有一个新的 AddBlogRating 迁移。 迁移文件名以时间戳作为前缀,这样有助于排序。 编辑生成的代码,为 Blog.Rating 指定默认值 3(下面代码中的第 10 行)
迁移还有一个代码隐藏文件,该文件可捕获某些元数据。 通过此元数据,Code First 迁移可以复制我们在此基于代码的迁移之前执行的自动迁移。 如果其他开发人员想要运行迁移,或者需要部署应用程序,这一点非常重要。
namespace MigrationsAutomaticDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class AddBlogRating : DbMigration
{
public override void Up()
{
AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
public override void Down()
{
DropColumn("Blogs", "Rating");
}
}
}
已编辑的迁移一切正常,所以我们可使用 Update-Database 来更新数据库。
- 在包管理器控制台中运行 Update-Database 命令。
返回自动迁移
现在,我们可自由切换回自动迁移,以简化更改。 Code First 迁移将根据其为每个基于代码的迁移存储在代码隐藏文件中的元数据,以正确的顺序执行自动迁移和基于代码的迁移。
- 将 Post.Abstract 属性添加到模型中
public string Abstract { get; set; }
现在,可使用 Update-Database 获取 Code First 迁移,通过自动迁移将此更改推送到数据库。
- 在包管理器控制台中运行 Update-Database 命令。
总结
在本演练中,你了解了如何使用自动迁移将模型更改推送到数据库。 还了解了如何在需要更多控制时在自动迁移之间构建基架并运行基于代码的迁移。