7.1 在我们的Movie模型中添加一个Rating(电影等级)属性
首先,我们在现存的Movie类中添加一个附加的“Rating”属性。打开Movie.cs文件,在Movie类中添加一个Rating属性,如下所示。
public string Rating { get; set; }
现在完整的Movie类的代码如代码清单7-1所示。
代码清单7-1 完整的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; }
}
点击“调试”菜单下的“生成MvcMovie”,重新编译应用程序。
现在我们已经将我们的模型进行了更新,让我们同样地修改我们的Views文件夹下的Movies文件夹下的Index.cshtml文件与Create.cshtml这两个视图模板文件,在视图中添加这个Rating属性。
首先打开Index.cshtml文件,在内容为“票价”(对应Price属性)的<th>元素后面追加“<th>电影等 级</th>”列标题(对应Rating属性)。在显示Price属性内容的td元素后面追加一个<td>元素,用来显示 Rating属性的内容。进行了这两个修改后的Index.cshtml文件中的主要内容如下所示。
<table>
<tr>
<th></th>
<th>
电影名称
</th>
<th>
发行日期
</th>
<th>
种类
</th>
<th>
票价
</th>
<th>
电影等级
</th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.ActionLink("编辑", "Edit", new { id=item.ID }) |
@Html.ActionLink("查看明细", "Details", new { id=item.ID }) |
@Html.ActionLink("删除", "Delete", new { id=item.ID })
</td>
<td>
@item.Title
</td>
<td>
@String.Format("{0:d}", item.ReleaseDate)
</td>
<td>
@item.Genre
</td>
<td>
@String.Format("{0:c2}", item.Price)
</td>
<td>
@item.Rating
</td>
</tr>
}
</table>
接下来打开Create.cshtml文件,在表单底部追加如下所示的标签。它将显示为一个文本框,用来输入Rating属性的内容。
<div class="editor-label">
电影等级
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Rating)
@Html.ValidationMessageFor(model => model.Rating)
</div>
7.2 维护模型与数据库结构之间的差别
现在我们已经将应用程序修改完毕,在Movie模型中添加了一个Rating属性。
现在让我们重新运行应用程序,打开“http://localhost:xx/Movies”这个URL地址,这时,浏览器会显示一个应用程序出错画面。如图7-1所示。
图7-1 应用程序出错画面
导致这个问题发生的原因是因为在我们的应用程序中,更新后的Movie模型类与我们实际连接的数据库的结构并不统一(Movies数据表中并没有Rating列)。
在默认情况下,当你使用EF code-first自动创建数据库时(就好像本教程中前面所介绍过的那样),EF code-first会自动在数据库中追加数据表来使得数据库的结构与它自动生成的模型类保持同步。如果不同步,EF将会抛出一个错误。这使得在开发程序时对于错误的跟踪会变得更加容易,否则你只能在运行时发现这个错误。同步检查特性正是引起以上显示的错误的原因。
有两种方法可以解决这个错误:
- 让Entity Framework自动删除当前数据库,并在新的模型类的基础上重新创建该数据库。这种方法在使用一个测试数据库时对于开发来说是十分方便的,因为它允许你快速地同步修改模型与数据库。但缺点是你将丢失现存库中的数据(所以请不要将这个方法使用在实际使用中的数据库上)。
- 修改数据库中的数据表的结构来使之与模型相匹配。这个方法的好处是可以让你保留表中的数据。你可以手工实现这一操作,或创建一个数据表修改脚本来实现这一操作。
在本教程中,我们使用第一种方法,在任何模型发生了改变的情况下让EF code-first自动重建数据库。
7.3 当模型改变时自动重建数据库
现在我们来修改我们的应用程序,使得我们的应用程序中如果任何模型发生了改变,都将自动删除与重建当前模型所使用的数据库。(请使用一个开发测试用的数据库来实践这一操作。用在实际使用的数据库中将造成库中数据的丢失)。
在解决方案资源管理器中,鼠标右击Modes文件夹,选择“添加”,然后点击“类”,如图7-2所示。
图7-2 添加一个新类
在“添加新项”对话框中,将类名定义为“MovieIntializer”,然后点击添加按钮添加该类。书写该类的代码如代码清单7-2所示。
代码清单7-2 MovieIntializer类的完整代码
using System;
using System.Collections.Generic;
using System.Data.Entity.Database;
namespace MvcMovie.Models
{
public class MovieIntializer :
DropCreateDatabaseIfModelChanges<MovieDBContext>
{
protected override void Seed(MovieDBContext context)
{
var movies = new List<Movie> {
new Movie { Title = "非诚勿扰 2",
ReleaseDate=DateTime.Parse("2011-1-11"),
Genre="爱情",
Rating="R",
Price=7.00M},
new Movie { Title = "赵氏孤儿",
ReleaseDate=DateTime.Parse("2011-2-23"),
Genre="历史",
Rating="R",
Price=9.00M},
};
movies.ForEach(d => context.Movies.Add(d));
}
}
}
使用这个MovieInitializer类之后,一旦我们的模型类发生改变后,我们的模型类所映射的数据库都会被自动重建。代码中使用了Seed方法来 指定任何重建数据库的时候想要默认追加到某张数据表中的数据。这为将一些示例数据添加到数据表中的操作提供了一个有用的方法,而不需要重建了数据库之后再 手工到数据表中添加示例数据。
现在我们已经定义好了我们的MovieInitializer类,接下来我们想在整个工程中使用这个类,这样每次在运行我们的应用程序的时候会自动检查当 前我们的模型类结构是否与数据库结构不一致,如果不一致的时候就自动重建该数据库,并且追加MovieInitializer类中所指定的默认数据。
打开我们的MvcMovies工程的根目录下的Global.asax文件,如图7-3所示。
图7-3 Global.asax文件
Global.asax文件中定义了当前工程所使用到的Application(应用程序)主类,包含了一个Application_Start()事件处理器,当第一次运行我们的应用程序时会触发这个事件。
让我们在文件头部追加两个有用的声明。第一个声明引用Entity Framework命名空间,第二个声明引用我们的MovieInitializer类所存在的命名空间。这两句声明的代码如下所示。
using System.Data.Entity.Database; // DbDatabase.SetInitialize
using MvcMovie.Models; // MovieInitializer
接下来寻找到Application_Start方法,在该方法的开头追加一个DbDatabase.SetInitializer()方法,代码如下所示。
protected void Application_Start()
{
DbDatabase.SetInitializer<MovieDBContext>(new MovieInitializer());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
我们追加的DbDatabase.SetInitializer方法将会在实际使用的数据库中的结构与我们的Movie模型类所映射的数据库结构不匹配时自动重建该数据库,并且自动添加MovieInitializer类中所指定的默认数据。
关闭Global.asax文件。
重新运行我们的应用程序,并在浏览器中输入“http://localhost:xx/Movies”。当我们的应用程序启动的时候,会自动觉察出我们的 模型类结构与数据库的结构不再匹配,于是重建该数据库使之相匹配,并且自动追加默认数据,浏览器中显示结果如图7-4所示。
图7-4 修改了模型类结构并追加默认数据后浏览器中的显示结果
点击追加按钮进行数据的追加,如图7-5所示。
图7-5 追加数据
点击追加按钮后,新追加的包括Rating(电影等级)字段的数据能正常追加并在电影清单画面中正常显示,如图7-6所示。
图7-6 包括Rating(电影等级)字段的数据能被正常追加与显示
7.4 修正票价字段的精度
在第六节追加数据的时候我们遗留下来一个问题,就是我们在追加数据的时候,票价(Price)字段中输入的是9.99元,但是电影清单显示画面中该数据的票价字段显示为10元,这是为什么?
这个问题发生的原因是因为当EF code-first在创建数据表的时候,如果字段为Decimal类型,则使用默认的精度(18:0),从而使得9.99元被四舍五入成为10元。现在我们想要将这个默认的精度修改为(18:2),从而使得数据表中的票价字段能够存储小数点后的两位数字。可喜的是EF code-first允许你很容易地重载这个定义模型如何向数据库中存取数据的映射规则。你可以利用这个重载机制来重载EF code-first中默认的类型定义以及数据表的继承规则,然后在这个基础上进行数据的存取。
为了改变我们的票价(Price)字段在数据表中的精度,打开Models文件夹下的Movie.cs文件。追加一句引用System.Data.Entity.ModelConfiguration的语句,代码如下所示。
using System.Data.Entity.ModelConfiguration;
在我们现在的MovieDBContext类中重载OnModelCreating方法,代码如下所示。
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Movie>().Property(p => p.Price).HasPrecision(18, 2);
}
}
OnModelCreating方法可以被用来重载与定制规定我们的模型类如何与我们的数据表进行映射的映射规则。代码中使用了EF的ModelBuilder API来定义我们的Movie对象的票价(Price)字段的精度为准确到小数点后两位。
现在完整的Movie.cs中的代码如代码清单7-3所示。
代码清单7-3 完整的Movie.cs中的代码
using System;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;
namespace MvcMovie.Models
{
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; }
}
public class MovieDBContext : DbContext
{
public DbSet<Movie> Movies { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Movie>().Property(p =>
p.Price).HasPrecision(18, 2);
}
}
}
现在让我们重新运行我们的应用程序,并且在浏览器中输入“http://localhost:xx/Movies”。当应用程序启动的时候,EF code-first将会再次察觉我们的模型类中的结构与数据表的结构不再匹配,然后自动重建数据表与新的模型类的结构进行匹配(具有新的票价(Price)字段的精度)。
重新创建一个新的电影(Movie)数据,在票价(Price)字段中输入9.99。请注意数据表中保存后显示在电影清单画面中的该条数据的票价(Price)字段中现在也显示为9.99了,如图7-7中所示。
图7-7 修改精度后电影清单画面中的显示结果
在本节中我们介绍了如何快速调整你的模型对象,同时在改变模型类的结构时自动将数据表中的结构保持同步。我们也介绍了如何预先在自动重建的数据表中追加默 认数据,它使得你可以快速在数据表中追加一些测试数据。在下一节中,我们将介绍如何在我们的模型类中加入我们自定义的数据有效性校验规则,从而强制实现一 些业务规则。