原文地址:http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx
原文名称:EF 4.3 Code-Based Migrations Walkthrough
准备工作
在开始之前,我们需要一个项目,以及一个 Code First 的模型,对于这次演示,我们使用典型的博客 Blog 和回复 Post 模型。
1. 创建新的 MigrationsCodeDemo 控制台应用程序
2. 为项目添加最新版本的 EntityFramework NuGet 包。
找到包管理器控制台。
运行 Install-Package EntityFramework 命令。
成功之后的项目文件如下:
创建初始的模型和数据库
1. 增加如下的 Model.cs 文件,代码定义了一个单个的 Blog 类表示我们的模型对象,BlogContext 类就是 EF Code First 的上下文。
using
System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsCodeDemo
{
publicclass BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
publicclass Blog
{
publicint BlogId { get; set; }
publicstring Name { get; set; }
}
}
2. 现在我们已经有了一个模型,是时候进行一次数据访问了。使用如下的代码更新 Program.cs 文件。
using
System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsCodeDemo
{
class Program
{
staticvoid 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);
}
}
}
}
}
3. 运行程序,现在就可以在你本地的 SQLExpress 数据库中看到数据库了。
启用迁移
1. 在 Blog 类中增加一个 Url 属性。
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
}
2. 现在运行程序,会看到如下的异常。
未经处理的异常: System.InvalidOperationException: 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).
3. 正如异常建议,现在是开始使用 Code First 迁移的时候了,第一步是为我们的上下文启用迁移支持。
在包管理器的控制台中执行命令:Enable-Migrations
4. 这个命令在项目中增加名为 Migrations 文件夹,文件夹中包含两个文件。
Configuration 类,这个类允许你配置你的上下文的迁移行为,在这个演示中,我们仅仅使用默认配置。
因为在这个项目中只有一个上下文,Enable-Migrations 自动填充应用的这个上下文。
201202171353373_InitialCreate.cs 中的 InitialCreate ,生成这个迁移是因为我们已经通过 Code First 创建了一个数据库。这个迁移中的代码表示我们已经创建的数据库,
namespace
MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
publicpartialclass InitialCreate : DbMigration
{
publicoverridevoid Up()
{
CreateTable(
"Blogs",
c => new
{
BlogId = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.BlogId);
}
publicoverridevoid Down()
{
DropTable("Blogs");
}
}
}
第一个迁移
你会使用到两个命令
Add-Migration,将会基于你对模型的修改创建一个迁移的脚手架。
Update-Database,将没有提交的修改提交到数据库。
1. 我们首先为我们新增加的 Url 属性创建一个迁移,Add-Migration 会帮我们完成这个工作,我们为这个迁移命名为:AddBlogUrl。
执行:Add-Migration AddBlogUrl
项目中增加了一个文件 201202171413369_AddBlogUrl.cs
内容如下:
namespace
MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
publicpartialclass AddBlogUrl : DbMigration
{
publicoverridevoid Up()
{
AddColumn("Blogs", "Url", c => c.String());
}
publicoverridevoid Down()
{
DropColumn("Blogs", "Url");
}
}
}
3. 我们可以在这个迁移中编辑或者增加内容,但是现在看起来就很不错,让我们将这个迁移提交到数据库中。
执行: Update-Database
4. 现在的数据库中已经包含了 Url 列。
定制迁移
下面我们看看如何定制迁移。
1. 在代码中增加一个回复的类 Post,同时在 Blog 中增加一个评分属性。
using
System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsCodeDemo
{
publicclass Blog
{
publicint BlogId { get; set; }
publicstring Name { get; set; }
publicstring Url { get; set; }
public int Rating { get; set; } public List<Post> Posts { 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; } }
}
2. 使用 Add-Migration 命令创建一个迁移,命名为 AddPostClass。
3. Code First Migrations 帮我们创建好了一个迁移,但是我们希望能增加如下的修改:
首先,为 Posts.Title 增加一个唯一索引。
增加一个非空的 Blogs.Rating 列,如果表中已经存在了数据,那么就将 CLR 默认的数据类型值赋予新增加的列 (Rating 是整数类型,应该就是 0), 但是,我们希望默认为 3, 这样已经存在的博客会有一个不错的评分。
namespace
MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
publicpartialclass AddPostClass : DbMigration
{
publicoverridevoid Up()
{
CreateTable(
"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("Blogs", t => t.BlogId, cascadeDelete: true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique: true);
AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
publicoverridevoid Down()
{
DropIndex("Posts", new[] { "BlogId" });
DropForeignKey("Posts", "BlogId", "Blogs");
DropColumn("Blogs", "Rating");
DropTable("Posts");
}
}
}
4. 运行 Update-Database 提交到数据库,这次可以使用一个参数 -Verbose,以便可以看到生成的 SQL 脚本。
数据操作/定制 SQL
我们已经完成的迁移并没有修改或者移动任何数据,我们可以在迁移中的任何地点运行任意脚本。
1. 在模型上增加一个摘要 PostAbstrack 属性,然后,希望使用已经发布的内容来填充这个摘要属性。
public
class
Post
{
publicint PostId { get; set; }
[MaxLength(200)]
publicstring Title { get; set; }
publicstring Content { get; set; }
public string Abstract { get; set; }publicint BlogId { get; set; }
public Blog Blog { get; set; }
}
2. 创建一个迁移。
3. 修改生成的迁移文件,
namespace
MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
publicpartialclass AddPostAbstract : DbMigration
{
publicoverridevoid Up()
{
AddColumn("Posts", "Abstract", c => c.String());
Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
publicoverridevoid Down()
{
DropColumn("Posts", "Abstract");
}
}
}
4. 更新数据库之后,就会得到最新的数据了。使用 Verbose 参数可以看到提交的脚本。
迁移到特定版本(包含降级)
你可以升级或者降级到特定的版本。
假设我们希望数据库迁移到执行 AddBlogUrl 迁移之后的状态,通过 -TargetMigration 参数来降级。
如果你希望回到最初的空数据库,可以使用参数 TargetMigration:$InitialDatabase.
获取迁移的脚本
在运行 Update-Database 的时候,使用参数 -Script 。
-SourceMigration:$InitialDatabase 用来指定起始的版本, -TargetMigration:"AddPostAbstract" 用来指定目标版本。
我们希望取得从初始的数据库,直到迁移到 AddPostAbstract 版本使用的脚本。
执行之后,得到一个脚本文件。
CREATE
TABLE
[Blogs]
(
[BlogId][int]NOTNULLIDENTITY,
[Name][nvarchar](max),
CONSTRAINT[PK_Blogs]PRIMARYKEY ([BlogId])
)
CREATETABLE[__MigrationHistory] (
[MigrationId][nvarchar](255) NOTNULL,
[CreatedOn][datetime]NOTNULL,
[Model][varbinary](max) NOTNULL,
[ProductVersion][nvarchar](32) NOTNULL,
CONSTRAINT[PK___MigrationHistory]PRIMARYKEY ([MigrationId])
)
BEGIN TRY
EXEC sp_MS_marksystemobject '__MigrationHistory'END TRY
BEGIN CATCH
END CATCH
INSERTINTO[__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171353373_InitialCreate', '2012-02-17T14:51:25.032Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F9B3DCE7E3BB8E09FAAC41DCDA660551DD9B2F7C96BF6B3B6C22ED5FE7ADD7B2F92875C08567C7CC61510C6C5F4E24EE8A4C18D9B93B203C8FBFC8562B228E274CFA49FA5A24E964FBF5FB4BCB4260DC9D3611A1B1D8DA9E8843B38BBCF32D754D983E2BEAA67D9AB5D924C3749DCC16BD66B7A0ACE9C9277057341DBD4D6BFC2E6FF4D5C9B82FEF0C45DF7D46035A9080F1D8746484474C4DE88BAFA75999D503427E5295EBC57293C2D80445E4CF87219FDC1E020B930F803FE8BFFFF86E67FC5D227B22A32D3B6AAF3B659BB8BDDBC4F66EB9BEC3DD8F95D36EB61F3DD693261FA54498CB6206B67B7DDDB4F9628C06E3D7BFA83C290B1AAF6BF045B62CCEF3A67D53BDCD610B4932BEBE09B24AB76966E5FF9BED500112DC68717A66EAF6F6637999D5D379566F2DB277773EC026BC079CF7D3F37DA575B3B61F54F6C2759F7D349B90FFF2461054F3F035ED405F021EDFF5FDACC74FF3A6B87020E0752DF329749F036ADA9C2DCF2B43621A918F9169D299812FF2369B11618EEBB638CFA62D7D3DCD9B86CDF34F66E59A9A9C2E26F9EC6CF9E5BA5DADDBE3A6C91793F2DA1FEFE3BB9BFB676317E2FCF8CB156BEF6F620884664143C8BF5C3E5917E5CCE2FDACCFCA4320C0232A1F8415B92704EEE2DA427A512D6F0948C9F7345FE54B48D79B7CB12A0958F3E5F27576990FE376330D438A3D7E5A646402173E05E513C5E475463D7B5D5007FE1BAE3FFA93D8959CFCA3FF270000FFFF2B65B379590C0000, '4.3.0')
ALTERTABLE[Blogs]ADD[Url][nvarchar](max)
INSERTINTO[__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171413369_AddBlogUrl', '2012-02-17T14:51:25.079Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F9B3DCE7E3BB8E09FAAC41DCDA660551DD9B2F7C96BF6B3B6C22ED5FE7ADD7B2F92875C08567C7CC61510C6C5F4E24EE8A4C18D9B93B203C8FBFC8562B228E274CFA49FA5A24E964FBF5FB4BCB4260DC9D3611A1B1D8DA9E8843B38BBCF32D754D983E2BEAA67D9AB5D924C3749DCC16BD66B7A0ACE9C9277057341DBD4D6BFC2E6FF4D5C9B82FEF0C45DF7D46035A9080F1D8746484474C4DE88BAFA75999D503427E5295EBC57293C2D80445E4CF87219FDC1E020B930F803FE8BFFFF86E67FC5D227B22A32D3B6AAF3B659BB8BDDBC4F66EB9BEC3DD8F95D36EB61F3DD693261FA54498CB6206B67B7DDDB4F9628C06E3D7BFA83C290B1AAF6BF045B62CCEF3A67D53BDCD610B4932BEBE09B24AB76966E5FF9BED500112DC68717A66EAF6F6637999D5D379566F2DB277773EC026BC079CF7D3F37DA575B3B61F54F6C2759F7D349B90FFF2461054F3F035ED405F021EDFF5FDACC74FF3A6B87020E0752DF329749F036ADA9C2DCF2B43621A918F9169D299812FF2369B11618EEBB638CFA62D7D3DCD9B86CDF34F66E59A9A9C2E26F9EC6CF9E5BA5DADDBE3A6C91793F2DA1FEFE3BB9BFB676317E2FCF8CB156BEF6F620884664143C8BF5C3E5917E5CCE2FDACCFCA4320C0232A1F8415B92704EEE2DA427A512D6F0948C9F7345FE54B48D79B7CB12A0958F3E5F27576990FE376330D438A3D7E5A646402173E05E513C5E475463D7B5D5007FE1BAE3FFA93D8959CFCA3FF270000FFFF2B65B379590C0000, '4.3.0')
CREATETABLE[Posts] (
[PostId][int]NOTNULLIDENTITY,
[Title][nvarchar](200),
[Content][nvarchar](max),
[BlogId][int]NOTNULL,
CONSTRAINT[PK_Posts]PRIMARYKEY ([PostId])
)
CREATEINDEX[IX_BlogId]ON[Posts]([BlogId])
CREATEUNIQUEINDEX[IX_Title]ON[Posts]([Title])
ALTERTABLE[Blogs]ADD[Rating][int]NOTNULLDEFAULT3ALTERTABLE[Posts]ADDCONSTRAINT[FK_Posts_Blogs_BlogId]FOREIGNKEY ([BlogId]) REFERENCES[Blogs] ([BlogId]) ONDELETECASCADEINSERTINTO[__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171424494_AddPostClass', '2012-02-17T14:51:25.092Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F943EFF31591031D6D9E930E8C17D96571C174EC407B59356DF351FA2A2F8593E7C54AB8778C6F7E7F66B8F4595D2D5E55A536E70F7FFF37597D91B78446D5FDE675B5AEA71D141EDF75BCBC91C301E6EB7038DEFB3A1C6EDEFBFF0087BF29DAF27D599C7AFA40762385D8D210DFB3DF0F67F3DBA99EDBB2B9F0F1FB73B9E1E53E971BFEBF0D971F374D352DB86B8FEDA4DB7000A7CB593A88834C80C19AE8BD2EDB62551653EAF2B38FBED5A3460C98155B074C461F02DB0DF122605F2E9FE665DEE6E9F114E320C6C89A6936EBCF01D160167E42529AD71093AC246E6ADA3A2B966D5FA48BE5B45865E510CA9D17DEC3CE01290BBEFBCDD37C952F21C44374FFB07E2DF80E916EA2C9E3BB1ED3F43526A492DEC86B0F0196D4775DED29ED5FE7ADD79214BE63D4800B7A1C14BEACD6A2F7B2F063E7656F0021049537EFFB9E3076E9B8492E2C9216BFDE346C9204EF75A54D97A1C38144E4DDCE86FDEEF15D7117F583C77707FCCAC75F64AB152954CFCFD44FD2D7E2649E6CBF7E7F47722130EE4E9B883F69B1B53D9169CB2EF2CEB750FDB3FC595137EDD3ACCD2619F4EDC96CD16B760BDE333DF92CD89F23C354A6357E9737FA9EF63