共用方式為


新增欄位

作者:Rick Anderson

注意

本教學課程的更新版本可在此取得,它使用最新版的 Visual Studio。 新的教學課程會使用 ASP.NET Core MVC,它在本教學課程提供多種改良。

本教學課程可讓您了解 ASP.NET Core MVC 與控制器和檢視。 Razor 頁面是 ASP.NET Core 中的新替代方案,它是以頁面為基礎的程式設計模型,可讓 Web UI 的建立更容易且更有效率。 建議您在嘗試使用 MVC 版本之前,先試試 Razor 頁面教學課程。 Razor 頁面教學課程:

  • 比較容易學習。
  • 涵蓋更多功能。
  • 是開發新應用程式的建議方法。

在本節,您會使用 Entity Framework Code First Migrations 將部分變更移轉至模型類別,以便讓變更套用至資料庫。

根據預設,使用 Entity Framework Code First 來自動建立資料庫時 (正如稍早在本教學課程操作的動作),Code First 會將表格新增至資料庫,以協助追蹤資料庫的結構描述是否與它產生的來源模型類別同步。 如果未同步,Entity Framework 會擲回錯誤。 這可讓您更輕鬆地在開發階段追蹤您可能只會在執行階段 (透過不明的錯誤) 發現的問題。

為模型變更設定 Code First Migrations

前往方案總管。 以滑鼠右鍵按一下 Movies.mdf 檔案,然後選取 [刪除] 移除電影資料庫。 如果您沒有看到 Movies.mdf 檔案,請按一下紅色外框下顯示的 [顯示所有檔案] 圖示。

螢幕擷取畫面所示為 MoviesController.cs 索引標籤,且方案總管開啟。[顯示所有檔案] 圖示以紅色圈選。

建立應用程式,確認沒有任何錯誤。

[工具] 功能表按一下 [NuGet 套件管理員],然後按一下 [套件管理員主控台]

新增套件管理員

「套件管理員主控台」視窗的 PM> 提示中輸入

Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext

螢幕擷取畫面所示為「套件管理員主控台」視窗。「啟用移轉」命令中的文字已醒目標示。

Enable-Migrations 命令 (如上所示) 會在新的 Migrations 資料夾中建立 Configuration.cs 檔案。

螢幕擷取畫面所示為方案總管。[Migrations] 資料夾的 [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,然後按一下 [using] [MvcMovie.Models;]

螢幕擷取畫面所示為 [顯示可能的修正] 功能表。[UsingMVCMovie.Models] 已選取,且顯示「找不到」警示。

這樣做會新增下列 using 陳述式:

using MvcMovie.Models;

注意

Code First Migrations 會在每次移轉後 (也就是在套件管理員主控台呼叫 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 Migrations 會在 Migrations 資料夾中建立另一個類別檔案 (名稱為 {DateStamp}_Initial.cs),且此類別包含的程式碼會建立資料庫結構描述。 移轉檔案名稱的前綴包含時間戳記,有助排序。 檢查 {DateStamp}_Initial.cs檔案,其中包為電影資料庫建立 Movies 表格的指示。 如果您在下方指示更新資料庫,此 {DateStamp}_Initial.cs 檔案就會執行並建立資料庫結構描述。 然後,Seed 方法會執行以便在資料庫填入測試資料。

「套件管理員主控台」輸入命令 update-database 以建立資料庫並執行 Seed 方法。

螢幕擷取畫面所示為套件管理員主控台。視窗中為更新資料庫的命令。

如果您收到錯誤,指出表格已存在且無法建立,可能是因為您在刪除資料庫之後及執行 update-database 之前執行應用程式。 在此情況下,請再次刪除 Movies.mdf 檔案,然後重試 update-database 命令。 如果仍然收到錯誤,請刪除移轉資料夾和內容,然後開始操作此頁面頂端的指示 (也就是刪除 Movies.mdf 檔案,然後繼續啟用移轉)。 如果仍然收到錯誤,請開啟 SQL Server 物件總管並從清單中移除資料庫。 如果您收到錯誤,指出「無法將檔案 .mdf 附加為資料庫」,請將 web.config 檔案連接字串裡的 Initial Catalog 屬性移除。

執行應用程式,並前往 /Movies URL。 Seed 資料隨即顯示。

螢幕擷取畫面所示為 MVC 電影索引,其中列出四部電影。

將 Rating 屬性新增至電影模型

首先,新增 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 類別,所以也需要更新繫結允許清單,才能加入這個新屬性。 更新Createbind 屬性和 Edit 動作方法,以便加入 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 檔案,並將 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 命令會告知移轉架構,檢查目前的電影模型與目前的電影資料庫結構描述,並建立必要的程式碼以將資料庫移轉至新的模型。 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 命令。

下圖顯示套件管理員主控台視窗的輸出 (日期戳記前的 Rating 將會不同。)

螢幕擷取畫面所示為已輸入更新資料庫命令的「套件管理員主控台」視窗。

重新執行應用程式,並前往 /Movies URL。 您可以看到新的「Rating」欄位。

螢幕擷取畫面所示為「MVC Movie Index」清單,其中已新增「Rating」欄位。

按一下 [新建] 連結以新增新電影。 請注意,您已可以新增評分。

7_CreateRioII

按一下 [建立]。 新的電影 (包括評分) 現在會在電影清單中顯示:

7_ourNewMovie_SM

既然專案已在使用移轉,您在新增欄位或更新結構描述時就不需要刪除資料庫了。 在下一節,我們會進行更多結構描述變更,並使用移轉來更新資料庫。

您也應將 Rating 欄位新增至 Edit、Details 和 Delete 檢視範本。

您可以再次在「套件管理員主控台」視窗輸入「update-database」命令,不會有任何移轉程式碼執行,因為結構描述符合模型。 不過,執行「update-database」會再次執行 Seed 方法,如果您變更任何 Seed 資料,則變更將會遺失,因為 Seed 方法會更新並插入資料。 您可以在 Tom Dykstra 熱門的 ASP.NET MVC/Entity Framework 教學課程深入瞭解 Seed 方法。

在本節中,您已瞭解如何修改模型物件,並讓資料庫與變更維持同步。 您也已透過範例資料的試用案例瞭解如何填入新建的資料庫。 本節只是 Code First 的快速簡介,請參閱為 ASP.NET MVC 應用程式 建立 Entity Framework 資料模型,針對本主題取得更完整的教學課程。 接下來,我們會說明如何將更豐富的驗證邏輯新增至模型類別,並啟用某些可強制執行的商務規則。