原文:https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/getting-started/introduction/adding-a-new-field
作者: Rick Anderson
ASP.NET MVC 5 入门
ASP.NET MVC 5 简介
此教程的更新版本可在此处使用最新版本的Visual Studio。 新教程使用ASP.NET CORE MVC,这在本教程中提供了许多改进。
本教程介绍具有控制器和视图的 ASP.NET Core MVC。 Razor 页面是 ASP.NET Core 2.0 中的一个新选择,它是基于页面的编程模型,可以实现更简单、更高效地生成 Web UI。 建议先尝试 Razor 页面教程,再使用 MVC 版本。 Razor 页面教程:
- 易于关注。
- 涵盖更多功能。
- 是开发新应用程序的首选方法。
以下教程系列介绍了GitHub上的 ASP.NET MVC: Source
- 入门
- 添加控制器
- 添加视图
- 添加模型
- 创建连接字符串并使用 SQL Server LocalDB
- 从控制器访问模型的数据
- 检查 Edit 方法和编辑视图
- 添加搜索
- 添加新字段
- 添加验证
- 检查 Details 和 Delete 方法
在本节中,您将使用 Entity Framework Code First 迁移将某些更改迁移到模型类,以便将更改应用到数据库。
默认情况下,当你使用实体框架 Code First 来自动创建数据库时(如在本教程前面的步骤中所做的那样),Code First 将向数据库中添加一个表,以帮助跟踪数据库的架构是否与生成它的模型类的架构同步。 如果不同步,实体框架将引发错误。 这样就可以在开发时更轻松地跟踪问题,否则在运行时可能仅会发现(隐藏错误)。
一、设置模型更改的 Code First 迁移
导航到解决方案资源管理器。 右键单击 "电影 .mdf " 文件,然后选择 "删除" 以删除电影数据库。 如果看不到 "电影 .mdf " 文件,请单击红色框中显示的 "显示所有文件" 图标。
生成应用程序,以确保没有任何错误。
在“工具”菜单中,单击“NuGet 包管理器”,然后单击“包管理器控制台”。
在 "包管理器控制台" 窗口中,在 PM>
提示符下输入
Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext
"启用-迁移" 命令(如上所示)在新的 "迁移" 文件夹中创建Configuration.cs文件。
Visual Studio 将打开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
}
);
}
将鼠标悬停在 Movie
下面的红色波浪线上,然后单击 "Show Potential Fixes
,然后单击"使用 MvcMovie "。
这样做将添加以下 using 语句:
using MvcMovie.Models;
Note
Code First 迁移将在每次迁移后调用 Seed
方法(即,在包管理器控制台中调用更新数据库),此方法将更新已插入的行,如果它们尚不存在,则将其插入。
以下代码中的AddOrUpdate方法执行 "upsert" 操作:
context.Movies.AddOrUpdate(i => i.Title,
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-1-11"),
Genre = "Romantic Comedy",
Rating = "PG",
Price = 7.99M
}
由于种子方法随每个迁移一起运行,因此您不能只插入数据,因为在第一个创建数据库的迁移之后,您尝试添加的行将已存在。 如果尝试插入已存在的行,"upsert" 操作将会阻止发生的错误,但会覆盖在测试应用程序时对数据所做的任何更改。 对于某些表中的测试数据,您可能不希望发生这种情况:在某些情况下,当您在测试时更改数据时,您希望在数据库更新后更改保持不变。 在这种情况下,需要执行条件插入操作:仅在不存在行时插入行。
传递给AddOrUpdate方法的第一个参数指定要用于检查行是否已存在的属性。 对于所提供的测试电影数据,Title
属性可用于此目的,因为列表中的每个标题都是唯一的:
context.Movies.AddOrUpdate(i => i.Title,
此代码假设标题是唯一的。 如果手动添加重复标题,则在下次执行迁移时,将会出现以下异常。
序列包含多个元素
有关AddOrUpdate方法的详细信息,请参阅使用 EF 4.3 AddOrUpdate 方法。
按 CTRL-SHIFT-B 生成项目。 (如果此时不生成,以下步骤将会失败。)
下一步是创建用于初始迁移的 DbMigration
类。 此迁移创建新的数据库,这就是在上一步中删除了movie .mdf文件的原因。
在 "程序包管理器控制台" 窗口中,输入命令 add-migration Initial
以创建初始迁移。 名称 "初始" 是任意名称,用于命名创建的迁移文件。
Code First 迁移在 "迁移" 文件夹(名称为 {日期戳}_Initial.cs )中创建另一个类文件,并且此类包含用于创建数据库架构的代码。 迁移文件名预先修复了时间戳,以帮助进行排序。 检查 {日期戳}_Initial.cs文件,其中包含为电影数据库创建 Movies
表的说明。 当你在下面的说明中更新数据库时,此 {日期戳}_Initial.cs文件将运行,并创建数据库架构。 然后, Seed方法将运行以用测试数据填充数据库。
在 "包管理器控制台" 中,输入用于创建数据库的命令 update-database
,并运行 Seed
方法。
如果您收到一条错误消息,指示表已存在并且无法创建,则可能是您在删除数据库之后和执行 update-database
之前运行了该应用程序。 在这种情况下,请再次删除电影 .mdf文件,然后重试 update-database
命令。 如果仍出现错误,请删除 "迁移" 文件夹和 "内容",然后从本页顶部的说明开始(删除 "",然后继续执行 "启用-迁移") 。 如果仍出现错误,请打开 SQL Server 对象资源管理器并从列表中删除数据库。
运行应用程序并导航到 /Movies URL。 将显示种子数据。
二、向电影模型添加分级属性
首先向现有 Movie
类添加新的 Rating
属性。 打开ModelsMovie.cs文件并添加 Rating
属性,如下所示:
public string Rating { get; set; }
完整的 Movie
类现在如以下代码所示:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
public string Rating { get; set; }
}
构建应用程序(Ctrl + Shift + B)。
由于已将新字段添加到 Movie
类,因此还需要更新 "绑定"允许列表,以便包括此新属性。 更新 Create
和 Edit
操作方法的 bind
属性,以包括 Rating
属性:
[Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")]
还需要更新视图模板,以便在浏览器视图中显示、创建和编辑新的 Rating
属性。
打开 " ViewsMoviesIndex.cshtml " 文件,然后在 " Price " 列之后添加 <th>Rating</th>
列标题。 然后,将 <td>
列添加到模板末尾附近,以呈现 @item.Rating
值。 更新后的索引 cshtml视图模板如下所示:
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm("Index", "Movies", FormMethod.Get))
{
<p>
Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" />
</p>
}
</p>
<table class="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>
接下来,打开 ViewsMoviesCreate.cshtml文件并添加 "Rating
" 字段,其中包含以下突出显示的标记。 这将呈现一个文本框,以便您可以在创建新电影时指定级别。
<div class="form-group">
@Html.LabelFor(model => model.Price, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Price, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Price, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Rating, new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Rating, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Rating, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
现在,你已更新了应用程序代码,以支持新的 Rating
属性。
运行应用程序并导航到 /Movies URL。 不过,当你执行此操作时,你将看到以下错误之一:
创建数据库后,支持 "MovieDBContext" 上下文的模型已发生更改。 请考虑使用 Code First 迁移更新数据库( https://go.microsoft.com/fwlink/?LinkId=238269)。
出现此错误的原因是,应用程序中更新的 Movie
模型类现在与现有数据库的 Movie
表的架构不同。 (数据库表中没有 Rating
列。)
可通过几种方法解决此错误:
- 让 Entity Framework 自动丢弃,并基于新的模型类架构重新创建数据库。 在测试数据库上进行开发时,此方法在开发周期早期很方便;通过它可以一起快速改进模型和数据库架构。 不过,缺点在于丢失了数据库中的现有数据,因此不希望在生产数据库上使用此方法! 使用初始值设定项,以使用测试数据自动设定数据库种子,这通常是开发应用程序的有效方式。 有关实体框架数据库初始值设定项的详细信息,请参阅ASP.NET MVC/实体框架教程。
- 对现有数据库架构进行显式修改,使它与模型类相匹配。 此方法的优点是可以保留数据。 可以手动或通过创建数据库更改脚本进行此更改。
- 使用 Code First 迁移更新数据库架构。
本教程使用 Code First 迁移。
更新种子方法,使其为新列提供一个值。 打开 MigrationsConfiguration.cs 文件,并向每个电影对象添加分级字段。
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-1-11"),
Genre = "Romantic Comedy",
Rating = "PG",
Price = 7.99M
},
生成解决方案,然后打开 "包管理器控制台" 窗口,并输入以下命令:
add-migration Rating
add-migration
命令通知迁移框架检查当前的电影模型和当前的 movie DB 架构,并创建必要的代码以将数据库迁移到新模型。 名称级别是任意的,用于对迁移文件进行命名。 为迁移步骤使用有意义的名称会很有帮助。
此命令完成后,Visual Studio 将打开定义新的 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");
}
}
生成解决方案,然后在 "包管理器控制台" 窗口中输入 update-database
命令。
下图显示了 "包管理器控制台" 窗口中的输出(预先计算评分的日期戳将有所不同)。
重新运行应用程序并导航到/Movies URL。 您可以看到新的 "分级" 字段。
单击 "新建" 链接以添加新电影。 请注意,可以添加级别。
单击“创建”。 现在电影列表中显示了新电影,其中包括评分:
现在,项目正在使用迁移,因此在添加新字段或更新架构时,无需删除数据库。 在下一部分中,我们将进行更多架构更改,并使用迁移来更新数据库。
还应将 "Rating
" 字段添加到 "编辑"、"详细信息" 和 "删除" 视图模板。
您可以在 "包管理器控制台" 窗口中再次输入 "更新数据库" 命令,而不会运行任何迁移代码,因为该架构与该模型匹配。 但是,运行 "更新数据库" 将再次运行 Seed
方法,如果更改了任何种子数据,则这些更改将丢失,因为 Seed
方法 upsert 数据。 若要详细了解 Tom Dykstra 的热门ASP.NET MVC/实体框架教程中的 Seed
方法。
在本部分中,您将了解如何修改模型对象并使数据库与更改保持同步。 还了解了使用示例数据填充新创建的数据库的方法,以便可以试用方案。 这只是 Code First 的简要介绍,请参阅为ASP.NET MVC 应用程序创建实体框架数据模型,以获取有关主题的更完整教程。 接下来,让我们看看如何将更丰富的验证逻辑添加到模型类,并实现一些业务规则。