添加新字段

作者 :Rick Anderson

注意

此处使用最新版本的 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 会向数据库添加一个表,以帮助跟踪数据库的架构是否与从中生成数据库的模型类同步。 如果它们不同步,实体框架将引发错误。 这样就可以更轻松地在开发时跟踪问题,否则在运行时可能只发现) 模糊错误 (的问题。

为模型更改设置Code First 迁移

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

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

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

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

添加 Pack Man

“包管理器控制台” 窗口中,在提示符处 PM> 输入

Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext

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

上面显示的 Enable-Migrations 命令 () 在新的 Migrations 文件夹中创建 Configuration.cs 文件。

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

Visual Studio 将打开 Configuration.cs 文件。 SeedConfiguration.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 ,然后单击 Show Potential Fixes使用MvcMovie.Models”;

显示“显示潜在修复”菜单的屏幕截图。已选择使用 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。

包管理器控制台中,输入命令 update-database 以创建数据库并运行 Seed 方法。

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

如果收到一个错误,指示表已存在且无法创建,则可能是因为在删除数据库之后以及执行 之前运行了 update-database应用程序。 在这种情况下,请再次删除 Movies.mdf 文件,然后重试该 update-database 命令。 如果仍然收到错误,请删除迁移文件夹和内容,然后从本页顶部的说明开始, (删除 Movies.mdf 文件,然后继续执行 Enable-Migrations) 。 如果仍然收到错误,请打开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更新 和 Edit 操作方法的 Create 属性,以包含 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 迁移更新数据库 ()”。

显示浏览器中显示通知“应用程序中服务器错误”的浏览器的屏幕截图。

出现此错误是因为应用程序中更新 Movie 的模型类现在不同于现有数据库表的 Movie 架构。 (数据库表中没有 Rating 列。)

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

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

本教程使用 Code First 迁移。

更新 Seed 方法,使其为新列提供值。 打开 Migrations\Configuration.cs 文件,并将 Rating 字段添加到每个 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 迁移到新模型所需的代码。 名称 Rating 是任意的,用于命名迁移文件。 为迁移步骤使用有意义的名称会很有帮助。

此命令完成后,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/实体框架教程中详细了解Seed该方法。

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