Setting up Code First Migrations for Model Changes--为模型更改做数据库迁移。
1.打开资源管理器,在App_Data文件夹下,找到movies.mdf数据库文件,如果没有看到点击显示所有文件。
2.删掉movies.mdf数据库文件,并编译项目。确保没有报错。
3.找到工具菜单栏下面的NuGet程序包管理器---程序包管理器控制台,如图所示:
4,在程序包管理器控制台中,输入:Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext
(注意: MvcMovie.Models.MovieDBContext 项目名.Models.项目数据上下文)
按enter键之后,可以看到:
5,数据库迁移之后,VS中自动为我们生成了一个Migrations文件夹,里面有一个Configuration.cs文件
6.打开Configuration.cs文件,引用命名空间:
using MvcMovie.Models;
然后在Seed方法中写上:
1 protected override void Seed(MvcMovie.Models.MovieDBContext context) 2 { 3 context.Movies.AddOrUpdate( i => i.Title, 4 new Movie 5 { 6 Title = "When Harry Met Sally", 7 ReleaseDate = DateTime.Parse("1989-1-11"), 8 Genre = "Romantic Comedy", 9 Price = 7.99M 10 }, 11 12 new Movie 13 { 14 Title = "Ghostbusters ", 15 ReleaseDate = DateTime.Parse("1984-3-13"), 16 Genre = "Comedy", 17 Price = 8.99M 18 }, 19 20 new Movie 21 { 22 Title = "Ghostbusters 2", 23 ReleaseDate = DateTime.Parse("1986-2-23"), 24 Genre = "Comedy", 25 Price = 9.99M 26 }, 27 28 new Movie 29 { 30 Title = "Rio Bravo", 31 ReleaseDate = DateTime.Parse("1959-4-15"), 32 Genre = "Western", 33 Price = 3.99M 34 } 35 ); 36 37 }
7.Code First Migrations calls the Seed
method after every migration (that is, calling update-database in the Package Manager Console), and this method updates rows that have already been inserted, or inserts them if they don‘t exist yet.
这句话的意思是:在每一次数据库迁移的时候,这个seed方法都会被调用,这个方法更新已经插入的行数据,或者插入行,如果这个行数据不存在。
8.下面的方法起到了一个更新插入的作用:
1 context.Movies.AddOrUpdate(i => i.Title, 2 new Movie 3 { 4 Title = "When Harry Met Sally", 5 ReleaseDate = DateTime.Parse("1989-1-11"), 6 Genre = "Romantic Comedy", 7 Rating = "PG", 8 Price = 7.99M 9 }
9.*因为Seed方法,在每次数据库迁移的时候,都会执行。你不能仅仅是插入数据,因为你将要插入的数据,将在第一次数据库迁移结束之后,已经存在数据库中;
*更新插入的操作可以预防错误,通过阻止你,插入已经存在的数据到数据库中。但是它有个缺点:它重载了,所有你在测试项目时候改变的数据;
因为有些测试数据,你不想改变:比如,你测试的时候,改变了数据,但是你不想这个数据在数据库更新的时候,发生改变。这个时候你可以做一个新增的操作:新增一个数据库中不存在的数据。
10.看一下这句代码吧:context.Movies.AddOrUpdate(i => i.Title,这第一个传到AddOrUpdate方法的参数,指定了一个属性,用来检查是否已经存在相同的行数据,对于我这个项目来说,我这个Title,可以做为这个属性,因为它在List中每次都是唯一的;This code assumes that titiles are unique. If you manually add a duplicate title, you‘ll get the following exception the next time you perform a migration.
Sequence contains more than one element 我们假想Title是唯一的,如果你手动添加了重复的Title,你将会在下次数据库迁移的时候,报一个错误,了解更多,请参考链接的文章,哈哈,纯人工翻译的哦。因为博主喜欢英语,所以还是看外国人的资料学习编程了。MSDN很不错的,
For more information about the AddOrUpdate method, see Take care with EF 4.3 AddOrUpdate Method..
11.现在我们来编译一下整个项目吧,如果这这里不编译的话,后面的步骤中将会出错误。
12.The next step is to create a DbMigration
class for the initial migration. This migration creates a new database, that‘s why you deleted the movie.mdf file in a previous step.
这句话的意思是:我们接下来要为初始化数据库迁移,创建一个DBMigration类,这个数据库迁移创建一个新的数据库,这也就是我们前面删掉Movie.mdf文件的原因。
13.在程序包管理器控制台中输入:
add-migration Initial
我们看到:
14.Code First Migrations creates another class file in the Migrations folder (with the name {DateStamp}_Initial.cs ), and this class contains code that creates the database schema. The migration filename is pre-fixed with a timestamp to help with ordering. Examine the {DateStamp}_Initial.cs file, it contains the instructions to create the Movies
table for the Movie DB. When you update the database in the instructions below, this {DateStamp}_Initial.cs file will run and create the the DB schema. Then the Seed method will run to populate the DB with test data.
这段话的意思是:Code First迁移,在Migration文件下,创建了另外一个类(类的文件名称是:时间_Initiaal.cs),并且这个类包含了创建数据库的代码。这个文件以时间的命名方式便于排序管理。检查这个文件,它包含了怎么为MovieDB创建Moviess数据库表。当你按照下面的指令(等会我在控制器管理控制台中输入的指定),更新数据库的时候,这个文件会执行,并且创建数据库,然后这个Seed方法,也将会执行,为数据库生成测试数据。
先看看看这个文件里面的代码是啥样的吧:
1 namespace MvcMovie.Migrations 2 { 3 using System; 4 using System.Data.Entity.Migrations; 5 6 public partial class Initial : DbMigration 7 { 8 public override void Up() 9 { 10 CreateTable( 11 "dbo.Movies", 12 c => new 13 { 14 ID = c.Int(nullable: false, identity: true), 15 Title = c.String(), 16 ReleaseDate = c.DateTime(nullable: false), 17 Genre = c.String(), 18 Price = c.Decimal(nullable: false, precision: 18, scale: 2), 19 }) 20 .PrimaryKey(t => t.ID); 21 22 } 23 24 public override void Down() 25 { 26 DropTable("dbo.Movies"); 27 } 28 } 29 }
同样看看我们之前的Migration里面Configuration.cs代码吧:
1 namespace MvcMovie.Migrations 2 { 3 using MvcMovie.Models; 4 using System; 5 using System.Data.Entity; 6 using System.Data.Entity.Migrations; 7 using System.Linq; 8 9 internal sealed class Configuration : DbMigrationsConfiguration<MvcMovie.Models.MovieDBContext> 10 { 11 public Configuration() 12 { 13 AutomaticMigrationsEnabled = false; 14 } 15 16 protected override void Seed(MvcMovie.Models.MovieDBContext context) 17 { 18 context.Movies.AddOrUpdate(i => i.Title, 19 new Movie 20 { 21 Title = "When Harry Met Sally", 22 ReleaseDate = DateTime.Parse("1989-1-11"), 23 Genre = "Romantic Comedy", 24 Price = 7.99M 25 }, 26 27 new Movie 28 { 29 Title = "Ghostbusters ", 30 ReleaseDate = DateTime.Parse("1984-3-13"), 31 Genre = "Comedy", 32 Price = 8.99M 33 }, 34 35 new Movie 36 { 37 Title = "Ghostbusters 2", 38 ReleaseDate = DateTime.Parse("1986-2-23"), 39 Genre = "Comedy", 40 Price = 9.99M 41 }, 42 43 new Movie 44 { 45 Title = "Rio Bravo", 46 ReleaseDate = DateTime.Parse("1959-4-15"), 47 Genre = "Western", 48 Price = 3.99M 49 } 50 ); 51 } 52 } 53 }
现在我们在,程序包管理器控制台中输入这个指令来创建数据库,并运行seed方法:
update-database
我们可以看到:
If you get an error that indicates a table already exists and can‘t be created, it is probably because you ran the application after you deleted the database and before you executed update-database
. In that case, delete theMovies.mdf file again and retry the update-database
command. If you still get an error, delete the migrations folder and contents then start with the instructions at the top of this page (that is delete the Movies.mdf file then proceed to Enable-Migrations).
这句话的意思是:如果你运行上面的update-database指定,初始化数据表错误显示:数据表已经存在不能被创建,很可能是因为你在删除movie.mdf数据库文件之后,运行了项目,没有先执行updata-database指令。这种情况下,你可以再次删除movie.mdf文件,然后重新执行update-database命令。如果仍然报错,删除这个Migration文件夹和里面的内容,重新按照我这篇文章刚开始的步骤,做一遍。。。
15.运行项目:在地址栏中输入Movie。(我这里就不输入Movie了,因为我改了路由配置,默认是Movie控制器下面的Index方法)可以看到seed方法里面的数据,都显示出来了。
16.现在让我们开始今天真正的任务:给Model添加一个新字段
打开Models文件夹下面的Movie.cs文件,在里面加入如图所示的字段:
接着我们编译一下项目吧。
17.Because you‘ve added a new field to the Movie
class, you also need to update the the binding white list so this new property will be included. Update the bind
attribute for Create
and Edit
action methods to include the Rating
property:
这句话的意思是:因为你新增了一个字段到Movie类中,你同样需要去为Create方法和Edit方法更新绑定Bind:
[Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")]
18.You also need to update the view templates in order to display, create and edit the new Rating
property in the browser view.
Open the \Views\Movies\Index.cshtml file and add a <th>Rating</th>
column heading just after the Price column. Then add a <td>
column near the end of the template to render the @item.Rating
value. Below is what the updated Index.cshtml view template looks like:
你同样需要去更新视图模板为了在新增和编辑的时候,去显示你刚才添加的字段。 打开\Views\Movies\Index.cshtml文件,在Price字段后面,去新增一个<th>Rating</th>列标题,然后在这个视图模板的后面,写上要显示的数据 @item.Rating,具体看图片所示;
19.Next, open the \Views\Movies\Create.cshtml file and add the Rating
field with the following highlighed markup. This renders a text box so that you can specify a rating when a new movie is created.
这句话的意思是:打开\Views\Movies\Create.cshtml 新增页面,添加Rating字段,使用下面的高亮显示的代码,这会生成一个文本框,所以在新增的时候,你可以指定一个Rating就可以添加到数据库中了。
如图:
现在已经完成了新增字段的功能任务了,我们来运行一下项目》》》
可以看到:
又报错了,数据库迁移方面的错误。。
20.You‘re seeing this error because the updated Movie
model class in the application is now different than the schema of the Movie
table of the existing database. (There‘s no Rating
column in the database table.)
这句话的意思是:你看到这个错误,是因为你更新了模型中的Movie,它现在和已经存在的Movie表中的不一样,已经存在的Movie表中,是没有Rating字段的!!!
21.
There are a few approaches to resolving the error:
- Have the Entity Framework automatically drop and re-create the database based on the new model class schema. This approach is very convenient early in the development cycle when you are doing active development on a test database; it allows you to quickly evolve the model and database schema together. The downside, though, is that you lose existing data in the database — so you don‘t want to use this approach on a production database! Using an initializer to automatically seed a database with test data is often a productive way to develope an application. For more information on Entity Framework database initializers, see Tom Dykstra‘s fantastic ASP.NET MVC/Entity Framework tutorial.
- Explicitly modify the schema of the existing database so that it matches the model classes. The advantage of this approach is that you keep your data. You can make this change either manually or by creating a database change script.
- Use Code First Migrations to update the database schema.
这里有一些方法,来解决这个问题:
1.让EF基于新的实体类,自动的删除和再创建数据库。这个方法在开发项目的早期,是很方便实用的。但是这会让你丢失之前已经存在数据库中的数据,所以使用一个初始化器,来存储数据库中的测试数据是很好的一个解决方法。要了解更多,请看链接文章。
2.根据你的Model结构,修改数据库结构。这个方法的优点是你的数据不会丢失。你可以手动修改数据库,或者写数据库脚本来修改。
3.使用Code First Migration技术,来升级数据库。
这个教程,我们使用方法3,即Code First Migration技术来升级数据库。
22.Update the Seed method so that it provides a value for the new column. Open Migrations\Configuration.cs file and add a Rating field to each Movie object.
打开Configuration文件,在里面添加我们刚才新增的Rating字段到每一个Movie对象中。
1 namespace MvcMovie.Migrations 2 { 3 using MvcMovie.Models; 4 using System; 5 using System.Data.Entity; 6 using System.Data.Entity.Migrations; 7 using System.Linq; 8 9 internal sealed class Configuration : DbMigrationsConfiguration<MvcMovie.Models.MovieDBContext> 10 { 11 public Configuration() 12 { 13 AutomaticMigrationsEnabled = false; 14 } 15 16 protected override void Seed(MvcMovie.Models.MovieDBContext context) 17 { 18 context.Movies.AddOrUpdate(i => i.Title, 19 new Movie 20 { 21 Title = "When Harry Met Sally", 22 ReleaseDate = DateTime.Parse("1989-1-11"), 23 Genre = "Romantic Comedy", 24 Price = 7.99M, 25 Rating="PG" 26 }, 27 28 new Movie 29 { 30 Title = "Ghostbusters ", 31 ReleaseDate = DateTime.Parse("1984-3-13"), 32 Genre = "Comedy", 33 Price = 8.99M, 34 Rating = "PG" 35 }, 36 37 new Movie 38 { 39 Title = "Ghostbusters 2", 40 ReleaseDate = DateTime.Parse("1986-2-23"), 41 Genre = "Comedy", 42 Price = 9.99M, 43 Rating = "PG" 44 }, 45 46 new Movie 47 { 48 Title = "Rio Bravo", 49 ReleaseDate = DateTime.Parse("1959-4-15"), 50 Genre = "Western", 51 Price = 3.99M, 52 Rating = "PG" 53 } 54 ); 55 } 56 } 57 }
Code First Migration
然后,我们编译一下项目。接着打开“程序包控制器管理台”,输入下面的指令:
add-migration Rating
23.The add-migration
command tells the migration framework to examine the current movie model with the current movie DB schema and create the necessary code to migrate the DB to the new model. The name Rating is arbitrary and is used to name the migration file. It‘s helpful to use a meaningful name for the migration step.
When this command finishes, Visual Studio opens the class file that defines the new DbMIgration
derived class, and in the Up
method you can see the code that creates the new column.
这个add-migration Rating指令,告诉migration框架,用当前的MovieDB数据结构,去检查当前的movie model,为数据库迁移创建必要的代码。这个Rating字段用来写这个添加指令。使用有意义的名字,来进行这个步骤,是很有必要的。当这个指令完成后,VS打开这个类文件,并定义一个部分类,来继承DbMigration类。看代码就知道了:
24.我们再次运行项目吧,激动人心的时刻来了。。。错!!!我们应该先要执行这个:
Build the solution, and then enter the update-database
command in the Package Manager Console window.(这一步坑死我了,我忘记执行了)。
新增字段运行新增的指令,之后,还得运行更新数据库的指令。就和db.SaveChanges()方法类似。这不执行完之后,才能是大功告成!!!
25.喜忧参半,之后,我们来运行项目吧。
可以看到:
这样就完成了数据库迁移技术的学习。欢迎大家评论,转载,我是@放飞梦想的翅膀,毕业于武汉软件工程职业学院。