【翻译】ASP.NET MVC4 入门(七) 为Movie实体和表添加一个新字段
在这一小节我们将会使用Entity Framework的Code First Migration模式来为model类带来一些改变,同时这些改变也会反映到数据库中。
默认地,当我们使用Entity Framework的Code First模式来自动创建数据库时(就像我们再前面的文章里所做的那样),Code First会想数据库中添加一张表来帮助我们跟踪数据库的schema与数据库所基于的模型类是否同步。如果他们不同步的话,Entity Framework会抛出一个错误。这样就会使在开发时发现问题变得很容易,而不是将问题带入运行时才发现。
设置Code First Migration来改变Model
如果你使用的是Visual Studio 2012,那么从解决方案资源管理器中双击Movies.mdf来打开数据库工具。Visual Studio Express将会显示Database Explorer,Visual Studio 2012将会显示Server Explorer。如果使用的是Visual Studio 2010,将会使用Sql Server Object Explorer。
在数据库工具中(Database Explorer,Server Explorer或者Sql Server Object Explorer),在Movie上右键单击选择Delete来删除这个数据库(如果是Visual Studio 2012的话则显示的是MovieDBContext)。
切换到解决方案资源管理器。在数据库文件上右键单击,选择Delete来移除数据库文件。
重新生成以下程序,确保没有编译错误。
从Tools菜单中选择"Library Package Manager"—>"Package Manager Console"
在"Package Manager Console"窗口中的PM>提示符后输入"Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContxt"(注意在Enable-Migrations后面有一个空格)。
Enable-Migrations命令会创建一个新的文件夹Migrations,并且在文件夹下创建一个Configuration.cs文件。
打开Configuration.cs文件,用下面的代码替换掉文件中的Seed方法。
protected override void Seed(MvcMovie.Models.MovieDBContext context) { context.Movies.AddOrUpdate( i => i.Title, new Movie { Title = "When Harry Met Sally", ReleaseDate = DateTime.Parse("1989-1-11"), Genre = "Romantic Comedy", Price = 7.99M }, new Movie { Title = "Ghostbusters ", ReleaseDate = DateTime.Parse("1984-3-13"), Genre = "Comedy", Price = 8.99M }, new Movie { Title = "Ghostbusters 2", ReleaseDate = DateTime.Parse("1986-2-23"), Genre = "Comedy", Price = 9.99M }, new Movie { Title = "Rio Bravo", ReleaseDate = DateTime.Parse("1959-4-15"), Genre = "Western", Price = 3.99M } ); }
Code First Migrations会在每一次迁移(也就是说在Package Manager控制台中调用update-database)之后调用Seed方法,这个方法会更新数据库中的行,如果没有就插入新行。
在进行下一步之前先把工程重新生成以下,否则的话下面的步骤会失败。
在Package Manager控制台窗口中,输入命令“add-migration Initial”来创建初始迁移。名字"Initial"只不过是随其起的,用来为创建的迁移文件命名。
Codi First Migrations在Migrations文件夹下创建了另外一个类文件(用{时间戳}_Initial.cs命名),这个类包含了创建数据库模式的代码。这个潜入文件用一个时间戳做前缀便于排序。查看一下{时间戳}_Initial.cs文件里的内容,包含了为Movie DB创建Movies表的指令。当我们使用下面的指令创建数据库时,{时间戳}_Initial.cs文件里的代码将会被执行来创建数据库模式,然后调用Seed方法来为数据库添加测试数据。
如果你看到了一个错误说数据库表已经存在无法被重复创建,这很可能是因为你在删除数据库之后,执行update-database命令之前运行了程序。在这种情况下,重新删除Movies.mdf文件再次执行update-database命令。如果仍然报错的话,删除migrations文件夹和文件夹下的文件,重复文章开始的步骤。
运行应用程序导航到/Movies,seed方法里添加的数据会被展示出来。
为Movie实体类添加一个Rating属性
让我们开始为Movie类添加一个Rating属性。打开Models\Movie.cs文件并且添加Rating属性。现在完整的Movie代码如下
public class Movie { public int ID { get; set; } public string Title { get; set; } public DateTime ReleaseDate { get; set; } public string Genre { get; set; } public decimal Price { get; set; }public string Rating { get; set
; }
}
重新生成以下项目。
现在我们已经更新了Model类,接下来更新以下\Views\Movies\Index.cshtml和\Views\Movies\Create.cshtml视图模板来显示这个新添加的属性。
打开、Views、Movies\Index.cshtml文件在Price列后面添加<th>Rating</th>.然后再模板文件结尾附近添加一个<td>来渲染@item.Rating的值。下面是更新后的Index.cshtml文件的内容。
@model IEnumerable<MvcMovie.Models.Movie> @{ ViewBag.Title = "Index"; } <h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> @Html.DisplayNameFor(model => model.Title) </th> <th> @Html.DisplayNameFor(model => model.ReleaseDate) </th> <th> @Html.DisplayNameFor(model => model.Genre) </th> <th> @Html.DisplayNameFor(model => model.Price) </th><th>
@Html.DisplayNameFor(model
=> model.Rating) </th>
<th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td><td>
@Html.DisplayFor(modelItem
=> item.Rating) </td>
<td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr> } </table>
接下来打开\Views\Movies\Create.cshtml文件在表单的结尾附近添加如下的标签,当一部新电影被创建时会渲染一个文本框出来这样用户可以指定一个分级。
<div class="editor-label"> @Html.LabelFor(model => model.Rating) </div> <div class="editor-field"> @Html.EditorFor(model => model.Rating) @Html.ValidationMessageFor(model => model.Rating) </div>
现在运行程序并且导航到/Movies,你会看到下面的错误:
看到这个错误的原因是Movie实体类和数据库中Movie表的结构不一致了,数据库中没有Rating字段。
有几种方法可以解决这个问题:
1.让Entity Framework自动删除并重新基于新的model类创建数据库。当在一个测试库上做开发的时候非常方便,可以让你非常快速的同步数据库和model。缺点就是你将会失去数据库中的所有数据——所以在生产环境的数据库中你是不可能使用这种方法的!使用一个初始化器来自动填充测试数据(前面提到的seed方法)在开发时非常有成效。关于Entity Framework数据库初始化器的更多内容,参见Tom Dykstra的 ASP.NET MVC/Entity Framework tutorial.
2.显示更改现存的数据库表结构来和model保持一致。有点是所有的数据都不会丢失,你可以手动修改或者写一些脚本来修改。
3.使用Code First Migrations来更新数据库结构。
在这篇文章中,我们使用Code First Migrations。
修改Seed方法来为新列提供一个值。打开Migrations\Configuration.cs文件为每一个Movie对象添加Rating属性的赋值。
new Movie { Title = "When Harry Met Sally", ReleaseDate = DateTime.Parse("1989-1-11"), Genre = "Romantic Comedy", Rating = "G", Price = 7.99M },
重新生成项目,然后打开Package Manager Console窗口输入下面的命令:add-migration AddRatingMig
add-migration命令告诉migration框架核查movie实体类和现有的Movie数据库,并且创建必须的代码来是数据库结构与model一致。AddRatingMig是随意起的名字来为migration文件命名,使用一个有意义的名字是非常有帮助的。
当这个命令实行完成后,打开定义新的DbMIgration类的文件,在Up方法中会看到为数据库添加新列的代码:
public partial class AddRatingMig : DbMigration { public override void Up() { AddColumn("dbo.Movies", "Rating", c => c.String()); } public override void Down() { DropColumn("dbo.Movies", "Rating"); } }
重新生成项目,在Package Manager Console窗口中输入"update-database"命令。
下面是Package Manager Console窗口的输出(在AddRatingMig前面的时间戳会不同)
重新运行程序,导航到/Movies,我们将会看到新添加的字段。
点击Create New链接来添加一部新电影,注意我们可以为Rating字段填一个值
点击Create按钮,新的Movies列表如下
同样也可以再Edit,Details和SearchIndex的视图模板中添加Rating字段。
这时候如果在Package Manager Console窗口中再次输入update-database命令的话,什么都不会发生,因为数据库表结构和model是一致的。
在这一小节我们知道了如何修改model对象并且是数据库和model保持一致。我们也介绍了一种为新建的数据库添加一些测试数据的方法。接下来,让我们看一看如何为model类添加丰富的逻辑验证规则。