用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署
用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署
这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第五篇:MVC程序中实体框架的Code First迁移和部署
原文:Code First Migrations and Deployment with the Entity Framework in an ASP.NET MVC Application
译文版权所有,谢绝全文转载——但您可以在您的网站上添加到该教程的链接。
到目前为止,应用程序已经可以在您本地机器上正常地运行。但如果您想将它发布在互联网上以便更多的人来使用,您需要将程序部署到WEB服务器。在本教程中你会将Contoso大学应用程序部署到Windows Azure网站的云中。
本教程包含以下章节:
- 启用Code First迁移。迁移功能能够使您不必删除并重新创建数据库的数据架构而进行更改数据模型并部署你的更改到生产环境下。
- 将应用程序部署到Windows Azure。该步骤是可选的,你可以跳过本步骤继续剩余的教程。
我们建议使用源代码管理的持续集成过程部署,但本教程并不包含那些主题。更多的信息请参见source control和Building Real-World Cloud Apps with Windows Azure。
启用Code First迁移
当你进行新应用程序的开发时,你的数据模型会频繁地变动。并且随着每一次变动都会使数据模型与数据库脱节。你已经成功配置了实体框架让其在每一次你变更数据模型时自动删除并重新创建数据库。当您添加、删除或更改实体类或者更改你的DbContext类时,重新运行应用程序会使它自动删除已经存在的数据库并创建一个和当前数据模型相匹配的数据库。并且填充测试数据。
这种方法在保持数据模型和数据库架构同步方面做得非常好,直到你准备将应用程序部署到生产环境。当应用程序开始生产并存储生产数据,你当然不想因为数据模型的变更而丢失成产数据(比如添加一个新列)。Code First Migrations功能解决了这个问题。通过启用Code First迁移来更新数据库架构,而不是删除和重建数据库。在本教程中,您会部署该应用程序,并准备启用迁移。
- 禁用之前教程中你在Web.Config中设定的初始设定项。

<entityFramework>
<!--<contexts>
<context type="ContosoUniversity.DAL.SchoolContext,ContosoUniversity">
<databaseInitializer type="ContosoUniversity.DAL.SchoolInitializer,ContosoUniversity"></databaseInitializer>
</context>
</contexts>-->
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
- 同样在Web.config中,更改数据库连接字符串的名为成ContosoUniversity2。
<connectionStrings>
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity1.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>此更改设置该项目的第一次迁移将创建一个新的数据库,这不是必须的,但您稍后将看到为这么这样做是一个不错的主意。
- 从工具菜单上,单击NuGet程序包管理器,单击程序包管理器控制台。

- 在控制台中输入以下命令:
enable-migrations
add-migration InitialCreate
enable-migrations命令将在项目中创建一个迁移文件夹。同时文件夹中包含一个Configuration.cs文件,你可以编辑该文件来配置迁移。
如果你在上一步中没有更改数据库名称,迁移将找到现有的数据库并自动执行add-migration命令,这没有关系。它只是意味着你不会在部署数据库之前运行迁移代码的测试。之后当您运行update-database将不会做任何改变因为数据库已经存在。
如同之前教程中,Configuration类中同样包含Seed方法。
internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
ContextKey = "ContosoUniversity.DAL.SchoolContext";
} protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
{
// This method will be called after migrating to the latest version. // You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
Seed方法的目的是使您在Code First创建或更新数据库后插入或更新测试数据。当数据库每次创建和更新数据库架构时将调用该方法。
设置Seed方法
当您每次更改数据模型后,删除和重新创建数据库时你可以使用初始类的Seed方法来插入测试数据。因为每次模型更改数据库后,数据库将被删除,所有的测试数据都将丢失。在Code First前一种,测试数据在数据库更改后是保留的。所以在Seed方法中包含测试数据通常不是必要的。事实上,你并不想要在使用迁移部署数据库到生产环境时让Seed方法来插入测试数据,因为Seed方法会在生产环境中调用。在这种情况下,只有真正需要时,才使用Seed方法来在生产环境中插入数据。例如你可能想要在部署到生产环境时在Deparment表中包含实际部门的名称。
对于本教程,您将使用迁移来部署。但为了让你能够跟容易地看到程序功能是如何无需人工操作而插入数据的,我们将使用Seed方法来插入测试数据。
- 使用下面的代码替换Configuration.cs文件的内容:

1 namespace ContosoUniversity.Migrations
2 {
3 using ContosoUniversity.Models;
4 using System;
5 using System.Collections.Generic;
6 using System.Data.Entity;
7 using System.Data.Entity.Migrations;
8 using System.Linq;
9
10 internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
11 {
12 public Configuration()
13 {
14 AutomaticMigrationsEnabled = false;
15 ContextKey = "ContosoUniversity.DAL.SchoolContext";
16 }
17 protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
18 {
19 var students = new List<Student>
20 {
21 new Student { FirstMidName = "Carson", LastName = "Alexander",
22 EnrollmentDate = DateTime.Parse("2010-09-01") },
23 new Student { FirstMidName = "Meredith", LastName = "Alonso",
24 EnrollmentDate = DateTime.Parse("2012-09-01") },
25 new Student { FirstMidName = "Arturo", LastName = "Anand",
26 EnrollmentDate = DateTime.Parse("2013-09-01") },
27 new Student { FirstMidName = "Gytis", LastName = "Barzdukas",
28 EnrollmentDate = DateTime.Parse("2012-09-01") },
29 new Student { FirstMidName = "Yan", LastName = "Li",
30 EnrollmentDate = DateTime.Parse("2012-09-01") },
31 new Student { FirstMidName = "Peggy", LastName = "Justice",
32 EnrollmentDate = DateTime.Parse("2011-09-01") },
33 new Student { FirstMidName = "Laura", LastName = "Norman",
34 EnrollmentDate = DateTime.Parse("2013-09-01") },
35 new Student { FirstMidName = "Nino", LastName = "Olivetto",
36 EnrollmentDate = DateTime.Parse("2005-08-11") }
37 };
38 students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
39 context.SaveChanges();
40
41 var courses = new List<Course>
42 {
43 new Course {CourseID = 1050, Title = "Chemistry", Credits = 3, },
44 new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
45 new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
46 new Course {CourseID = 1045, Title = "Calculus", Credits = 4, },
47 new Course {CourseID = 3141, Title = "Trigonometry", Credits = 4, },
48 new Course {CourseID = 2021, Title = "Composition", Credits = 3, },
49 new Course {CourseID = 2042, Title = "Literature", Credits = 4, }
50 };
51 courses.ForEach(s => context.Courses.AddOrUpdate(p => p.Title, s));
52 context.SaveChanges();
53
54 var enrollments = new List<Enrollment>
55 {
56 new Enrollment {
57 StudentID = students.Single(s => s.LastName == "Alexander").ID,
58 CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
59 Grade = Grade.A
60 },
61 new Enrollment {
62 StudentID = students.Single(s => s.LastName == "Alexander").ID,
63 CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
64 Grade = Grade.C
65 },
66 new Enrollment {
67 StudentID = students.Single(s => s.LastName == "Alexander").ID,
68 CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
69 Grade = Grade.B
70 },
71 new Enrollment {
72 StudentID = students.Single(s => s.LastName == "Alonso").ID,
73 CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
74 Grade = Grade.B
75 },
76 new Enrollment {
77 StudentID = students.Single(s => s.LastName == "Alonso").ID,
78 CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
79 Grade = Grade.B
80 },
81 new Enrollment {
82 StudentID = students.Single(s => s.LastName == "Alonso").ID,
83 CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
84 Grade = Grade.B
85 },
86 new Enrollment {
87 StudentID = students.Single(s => s.LastName == "Anand").ID,
88 CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
89 },
90 new Enrollment {
91 StudentID = students.Single(s => s.LastName == "Anand").ID,
92 CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
93 Grade = Grade.B
94 },
95 new Enrollment {
96 StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
97 CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
98 Grade = Grade.B
99 },
100 new Enrollment {
101 StudentID = students.Single(s => s.LastName == "Li").ID,
102 CourseID = courses.Single(c => c.Title == "Composition").CourseID,
103 Grade = Grade.B
104 },
105 new Enrollment {
106 StudentID = students.Single(s => s.LastName == "Justice").ID,
107 CourseID = courses.Single(c => c.Title == "Literature").CourseID,
108 Grade = Grade.B
109 }
110 };
111
112 foreach (Enrollment e in enrollments)
113 {
114 var enrollmentInDataBase = context.Enrollments.Where(
115 s =>
116 s.Student.ID == e.StudentID &&
117 s.Course.CourseID == e.CourseID).SingleOrDefault();
118 if (enrollmentInDataBase == null)
119 {
120 context.Enrollments.Add(e);
121 }
122 }
123 context.SaveChanges();
124 }
125 }
126 }
Seed方法使用数据库上下文对象作为输入参数,并在代码中使用该对象来添加新实体到数据库。对于每个实体类型,代码创建一个新实体的集合并将它们添加到适当的DbSet属性,然后将更改保存到数据库。在每组实体后立刻调用SaveChanges方法并不是必须的,但这样做可以在出现问题时让你更容易地定位问题的根源。
大多数插入对象的语句是使用AddOrUpdate方法来执行"upsert"操作。因为你每次执行更新数据库命令时Seed方法都会运行,通常在每个迁移后你不能只是插入数据。因为您试图添加的行有可能在创建数据库后的第一次迁移中已经存在。"upsert"操作可以防止你试图添加一个已经存在的行,但是它会重写你在测试阶段对数据进行的修改。你或许不希望这种情况在某些数据表中发生:在某些情况下你可能希望保留你在测试阶段对测试数据所进行的更改。在这种情况下,你需要做一个条件插入操作:仅当它不存在时插入行。Seed方法同时使用以上两种方法。
第一个传递给AddOrUpdate方法的参数,一个指定的属性是用来检查否行已经存在。对于您提供的测试学生数据,LastName属性可以被用作检查在每个列表中实体是否是唯一的。context.Students.AddOrUpdate(p => p.LastName, s)
此代码假定LastName是唯一的。如果您手动添加具有重复LastName的学生,你就会得到一个“序列包含多个元素”的异常。
有关如何处理容易数据,请参阅Seeding and Debugging Entity Framework (EF) DBs。有关AddOrUpdate方法的更多信息,请参阅Take care with EF 4.3 AddOrUpdate Method。
创建Enrollment实体的代码假定你在学生集合中的实体已经拥有ID值,虽然你没有在创建集合的代码中设置该值。new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
Grade = Grade.B
},你在这里可以使用ID值,因为当你为学生集合调用SaveChanges方法时,ID值被设置。当实体被插入到数据库时,实体框架会自动获取该实体的主键值并且更新内存中实体上的ID属性。
添加每个Enrollment实体到Enrollments实体集合的代码不会使用AddOrUpdate方法,它会检查每一个实体是否存在,如果不存在,则插入该实体。这种方法将保留通过使用应用程序UI对成绩所做的修改。代码遍历Enrollment列表中的每个成员。如果在数据库中没有该成员,就向数据库中添加它。当你第一次更新数据库时,该数据库是空的,所以集合中的每个enrollment实体都将被添加。
foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.ID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
- 生成项目。
执行第一次迁移
当您执行add-migration命令时,迁移将生成代码用来创建数据库。该代码同样在Migrations文件夹中,在文件名为<时间戳>_InitalCreate.cs的文件中。该类中的Up方法将按照数据模型实体集来创建数据库表格,Down方法用来删除它们。

namespace ContosoUniversity.Migrations
{
using System;
using System.Data.Entity.Migrations; public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Course",
c => new
{
CourseID = c.Int(nullable: false),
Title = c.String(),
Credits = c.Int(nullable: false),
})
.PrimaryKey(t => t.CourseID); CreateTable(
"dbo.Enrollment",
c => new
{
EnrollmentID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
StudentID = c.Int(nullable: false),
Grade = c.Int(),
})
.PrimaryKey(t => t.EnrollmentID)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
.Index(t => t.CourseID)
.Index(t => t.StudentID); CreateTable(
"dbo.Student",
c => new
{
ID = c.Int(nullable: false, identity: true),
LastName = c.String(),
FirstMidName = c.String(),
EnrollmentDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.ID); } public override void Down()
{
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
DropIndex("dbo.Enrollment", new[] { "StudentID" });
DropIndex("dbo.Enrollment", new[] { "CourseID" });
DropTable("dbo.Student");
DropTable("dbo.Enrollment");
DropTable("dbo.Course");
}
}
}

迁移调用Up方法来实现数据模型所做的更改。当你输入一个命令回滚更新,迁移将调用Down方法。
这是您输入add-migration InitialCreate命令时创建的初始迁移。参数(在该示例中是InitialCreate)用于文件的名称,当然也可以是任意你想要的其他名称。通常你会选择一个单词或短语来总结迁移中所做的改变。例如您可以能会命名之后的迁移为"AddDeparmentTable"。
如果你创建了一个在数据库已经存在的情况下的迁移,则生成的的数据库创建代码不会运行。因为数据库已经和数据模型匹配。将应用程序部署到另一个尚未创建数据库的环境时,代码才会运行以创建数据库。所以最好是提前测试一下。这就是为什么之前你更改了连接字符串中数据库的名称,以便迁移可以从零开始创建一个新的数据库。
- 在程序包管理器控制台中,输入以下命令:
update-database

update-database命令运行Up方法来创建数据库,然后运行Seed方法来填充数据库。同样的过程会在将程序部署到生产环境下发生,您将会在下一节看到。
- 使用服务器资管管理器来检查数据库,验证里面的数据和程序同之前一样运行正常。
部署到Windows Azure
因为咱没有Windows Azure的试用账号,所以这部分翻译就跳过了……反正也不影响学习的。
总结
在本节中你看到了如何使用Code First迁移,在下一节中你会开始进入高级主题,扩展数据模型。
作者信息
Tom Dykstra - Tom Dykstra是微软Web平台及工具团队的高级程序员,作家。
用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的Code First迁移和部署的更多相关文章
- MVC程序中实体框架的Code First迁移和部署
01. 启用迁移 [在控制台中,输入以下命令:]enable-migrations //命令将在项目中创建一个迁移文件夹.同时文件夹中包含一个Configuration.cs文件,你可以编辑该文件来 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:MVC程序中实体框架的连接恢复和命令拦截
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第四篇:MVC程序中实体框架的连接恢复和 ...
- MVC程序中实体框架的连接恢复和命令拦截
MVC程序中实体框架的连接恢复和命令拦截 这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC ...
- [转] 使用 MVC 5 的 EF6 Code First 入门 系列
译文:http://www.cnblogs.com/Bce-/category/573301.html 原文:http://www.asp.net/mvc/overview/getting-start ...
- MVC 5 的 EF6 Code First 入门 系列:排序、筛选和分页
这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第三篇:排序.筛选 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:实现基本的CRUD功能
英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with Entity Framework 6 Code First using MVC 5 系列 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:排序、筛选和分页
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第三篇:排序.筛选和分页 原文:Sort ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序创建更复杂的数据模型
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第六篇:为ASP.NET MVC应用程序 ...
- [渣译文] 使用 MVC 5 的 EF6 Code First 入门 系列:为ASP.NET MVC应用程序读取相关数据
这是微软官方教程Getting Started with Entity Framework 6 Code First using MVC 5 系列的翻译,这里是第七篇:为ASP.NET MVC应用程序 ...
随机推荐
- [2013山东ACM]省赛 The number of steps (可能DP,数学期望)
The number of steps nid=24#time" style="padding-bottom:0px; margin:0px; padding-left:0px; ...
- 使用 CodeIgniter 创建一个简单的 Web 站点
原文:使用 CodeIgniter 创建一个简单的 Web 站点 参考源自: http://www.ibm.com/developerworks/cn/web/wa-codeigniter/index ...
- Spring Security 入门详解(转)
1.Spring Security介绍 Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别 处理 ...
- MongoDB初学者的配置环境和基础设施的使用
一个.mongoDB安装 1.第一MongoDB官方网站下载安装包http://www.mongodb.org/. 依照自己的操作系统进行下载. 2.在硬盘上建立两个文件夹分辨存放mongoDB安装文 ...
- DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表
原文:DevExpress XtraReports 入门二 创建 data-aware(数据感知) 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用的, ...
- 它可以作为一个代理server或者转发java类
在项目中使用,这简化和通用汽车.突出的基本思路,细节可以基于此类改变. 基于java容器和servlet. package com.xxx.first; import java.io.Buffered ...
- 如何构建高性能web网站:分布式缓存
一.数据库前端缓冲区 要清除数据库前缓冲区,首先必须明确什么是文件系统的内核缓冲区(Buffer Area):它位于内核的物理内存地址空间,除了使用O_DIRECT比其他标签中打开文件,所有的磁盘的读 ...
- Atitit.软体guibuttonand面板---通信子系统(范围)-- github 采用....
Atitit.软体guibuttonand面板---通讯子系统(区)-- github 的使用.... 1. 1.注冊账户以及创建仓库 1 2. 二.在GitHub中创建项目(create a new ...
- 使用 CXF 做 webservice 简单例子[转]
Apache CXF 是一个开放源代码框架,提供了用于方便地构建和开发 Web 服务的可靠基础架构.它允许创建高性能和可扩展的服务,您可以将这样的服务部署在 Tomcat 和基于 Spring 的轻量 ...
- mysql_navicat-permium 在Mac下破解方法
首先下载符合当前系统支持的navicat-permium版本,我自己下载的是11.0.16 然后我们开始破解旅程,先要安装上navicat-permium,记住千万不要打开(如果你打开了不好意思,卸了 ...