添加新字段

作者: 里克·安德森

注意

本教程的更新版本是使用最新版本的 Visual Studio 提供的。 新教程使用 ASP.NET Core MVC,这为本教程提供了 许多 改进。

本教程介绍具有控制器和视图的 ASP.NET Core MVC。 Razor Pages 是 ASP.NET Core 中的一种新替代方法,它是一种基于页面的编程模型,使生成 Web UI 更加轻松高效。 建议先尝试 Razor 页面教程,再使用 MVC 版本。 Razor 页面教程:

  • 易于关注。
  • 涵盖更多功能。
  • 是新应用开发的首选方法。

在本部分中,你将使用Entity Framework Code First 迁移将一些更改迁移到模型类,以便将更改应用于数据库。

默认情况下,使用 Entity Framework Code First 自动创建数据库时,如本教程前面所述,Code First 向数据库添加一个表,以帮助跟踪数据库的架构是否与生成的模型类同步。 如果它们未同步,则 Entity Framework 将引发错误。 这样就可以更轻松地在开发时跟踪你可能只在运行时发现(通过模糊错误)的问题。

为模型更改设置Code First 迁移

导航到解决方案资源管理器。 右键单击Movies.mdf文件,然后选择“删除以删除电影数据库。 如果未看到Movies.mdf文件,请单击红色轮廓中显示的“显示所有文件图标。

显示电影控制器点 c 选项卡并解决方案资源管理器打开的屏幕截图。“显示所有文件”图标以红色圆圈。

生成应用程序,以确保没有任何错误。

在“工具”菜单中,单击“NuGet 包管理器”,然后单击“包管理器控制台”

添加 Pack Man

提示符处的“程序包管理器控制台”窗口中PM>

Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext

显示程序包管理器控制台窗口的屏幕截图。突出显示了“启用迁移”命令中的文本。

Enable-Migrations 命令(如上所示)在新迁移文件夹中创建Configuration.cs文件。

显示解决方案资源管理器的屏幕截图。已选择迁移文件夹的配置点 c 子文件夹。

Visual Studio 将打开 Configuration.cs 文件。 将 Seed Configuration.cs 文件中的方法替换为以下代码:

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并单击,然后单击“使用 MvcMovie.Models”;Show Potential Fixes

显示“显示潜在修复”菜单的屏幕截图。已选择使用 M V C 电影点模型,并且无法找到警报。

这样做会添加以下 using 语句:

using MvcMovie.Models;

注意

Code First 迁移每次迁移(即在 程序包管理器 控制台中调用 update-database)后调用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
    }

由于 Seed 方法在每次迁移时都运行,因此不能只插入数据,因为尝试添加的行在创建数据库的第一次迁移之后就已存在。 如果尝试插入已存在的行,则“upsert”操作会阻止发生错误,但它会替代测试应用程序时对数据所做的任何更改。 对于某些表中的测试数据,你可能不希望发生这种情况:在某些情况下,在测试时更改数据时,希望更改在数据库更新后保留。 在这种情况下,需要执行条件插入操作:仅当行尚不存在时插入行。

传递给 AddOrUpdate 方法的第一个参数指定用于检查是否存在行的属性。 对于您提供的测试电影数据,该属性可用于此目的, Title 因为列表中的每个标题都是唯一的:

context.Movies.AddOrUpdate(i => i.Title,

此代码假定游戏是唯一的。 如果手动添加重复的标题,下次执行迁移时,将看到以下异常。

序列包含多个元素

有关 AddOrUpdate 方法的详细信息,请参阅 EF 4.3 AddOrUpdate 方法

按 Ctrl-Shift-B 生成项目。(如果此时未生成,以下步骤将失败。

下一步是为初始迁移创建 DbMigration 类。 此迁移会创建一个新数据库,这就是在上一步中删除 movie.mdf 文件的原因。

“程序包管理器控制台”窗口中,输入用于创建初始迁移的命令add-migration Initial。 名称“Initial”是任意名称,用于命名创建的迁移文件。

显示程序包管理器控制台的屏幕截图。“添加迁移”命令中的文本突出显示。

Code First 迁移在 另一个类文件迁移文件夹(名称为 {DateStamp}_Initial.cs),此类包含创建数据库架构的代码。 迁移文件名以时间戳作为前缀,这样有助于排序。 检查 {DateStamp}_Initial.cs文件,其中包含为 Movie DB 创建Movies表的说明。 在以下说明中更新数据库时,此 {DateStamp}_Initial.cs 文件将运行并创建 DB 架构。 然后, Seed 方法将运行以使用测试数据填充 DB。

程序包管理器控制台中,输入用于创建数据库并运行该方法的Seed命令update-database

显示程序包管理器控制台的屏幕截图。更新数据库命令位于窗口中。

如果收到一个错误,指出表已存在且无法创建,则可能是因为在删除数据库后以及执行 update-database之前运行了应用程序。 在这种情况下,请再次删除 Movies.mdf 文件,然后重试 update-database 该命令。 如果仍收到错误,请删除迁移文件夹和内容,然后从此页面顶部的说明开始(即删除 Movies.mdf 文件,然后继续启用迁移)。 如果仍然收到错误,请打开 SQL Server 对象资源管理器并从列表中删除数据库。 如果收到指示“无法将文件.mdf作为数据库附加”的错误,请在 web.config 文件中删除初始目录属性作为连接字符串的一部分。

运行应用程序并导航到 /Movies URL。 将显示种子数据。

显示列出四部电影的 M V C 电影索引的屏幕截图。

向电影模型添加分级属性

首先向 Rating 现有 Movie 类添加新属性。 打开 Models\Movie.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 ,因此还需要更新绑定 允许列表 ,以便包含此新属性。 更新 bind 属性 CreateEdit 操作方法以包括 Rating 属性:

[Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")]

还需要更新视图模板,以便在浏览器视图中显示、创建和编辑新的 Rating 属性。

打开 \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")
    @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>

接下来,打开 \Views\Movies\Create.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 列。)

可通过几种方法解决此错误:

  1. 让 Entity Framework 自动丢弃,并基于新的模型类架构重新创建数据库。 在测试数据库上进行开发时,此方法在开发周期早期很方便;通过它可以一起快速改进模型和数据库架构。 不过,缺点是,在数据库中丢失现有数据,因此 你不想 在生产数据库中使用此方法! 使用初始值设定项,以使用测试数据自动设定数据库种子,这通常是开发应用程序的有效方式。 有关 Entity Framework 数据库初始值设定项的详细信息,请参阅 ASP.NET MVC/Entity Framework 教程
  2. 对现有数据库架构进行显式修改,使它与模型类相匹配。 此方法的优点是可以保留数据。 可以手动或通过创建数据库更改脚本进行此更改。
  3. 使用 Code First 迁移更新数据库架构。

本教程使用 Code First 迁移。

更新 Seed 方法,以便为新列提供值。 打开 Migrations\Configuration.cs 文件,并将“评级”字段添加到每个 Movie 对象。

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 命令告知迁移框架使用当前电影 DB 架构检查当前电影模型,并创建必要的代码将 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。 可以看到新的“评分”字段。

显示添加了“评分”字段的 M V C 电影索引列表的屏幕截图。

单击“新建”链接添加新电影。 请注意,可以添加分级。

7_CreateRioII

单击 “创建” 。 新电影,包括评级,现在显示在电影列表:

7_ourNewMovie_SM

现在,项目正在使用迁移,在添加新字段或更新架构时,无需删除数据库。 在下一部分中,我们将进行更多架构更改,并使用迁移来更新数据库。

还应将 Rating 字段添加到“编辑”、“详细信息”和“删除”视图模板。

你可以再次 程序包管理器在“控制台”窗口中输入“update-database”命令,并且不会运行任何迁移代码,因为架构与模型匹配。 但是,运行“update-database”将再次运行 Seed 该方法,如果更改了任何种子数据,则更改将丢失,因为 Seed 该方法会更新插入数据。 可以在 Tom Dykstra 的热门 ASP.NET MVC/Entity Framework 教程中详细了解该方法Seed

本部分介绍了如何修改模型对象,并使数据库与更改保持同步。 你还了解了使用示例数据填充新创建的数据库的方法,以便可以试用方案。 这只是 Code First 的快速简介,请参阅 为 ASP.NET MVC 应用程序 创建实体框架数据模型,获取有关主题的更完整的教程。 接下来,让我们看看如何向模型类添加更丰富的验证逻辑,以及如何强制实施一些业务规则。