原文連結:http://www.asp.net/mvc/tutorials/mvc-4/getting-started-with-aspnet-mvc4/adding-a-new-field-to-the-movie-model-and-table
在這一小節我們將會使用Entity Framework的Code First Migration模式來為model類帶來一些改變,同時這些改變也會反映到資料庫中。
預設地,當我們使用Entity Framework的Code First模式來自動建立資料庫時(就像我們再前面的文章裡所做的那樣),Code First會想資料庫中添加一張表來協助我們追蹤資料庫的schema與資料庫所基於的模型類是否同步。如果他們不同步的話,Entity Framework會拋出一個錯誤。這樣就會使在開發時發現問題變得很容易,而不是將問題帶入運行時才發現。
設定Code First Migration來改變Model
如果你使用的是Visual Studio 2012,那麼從方案總管中雙擊Movies.mdf來開啟資料庫工具。Visual Studio Express將會顯示Database Explorer,Visual Studio 2012將會顯示Server Explorer。如果使用的是Visual Studio 2010,將會使用Sql Server Object Explorer。
在資料庫工具中(Database Explorer,Server Explorer或者Sql Server Object Explorer),在Movie上按右鍵選擇Delete來刪除這個資料庫(如果是Visual Studio 2012的話則顯示的是MovieDBContext)。
切換到方案總管。在資料庫檔案上按右鍵,選擇Delete來移除資料庫檔案。
重建以下程式,確保沒有編譯錯誤。
從Tools菜單中選擇"Library Package Manager"—>"Package Manager Console"
在"Package Manager Console"視窗中的PM>提示符後輸入"Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContxt"(注意在Enable-Migrations後面有一個空格)。
Enable-Migrations命令會建立一個新的檔案夾Migrations,並且在檔案夾下建立一個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 } ); }
Code First Migrations會在每一次遷移(也就是說在Package Manager控制台中調用update-database)之後調用Seed方法,這個方法會更新資料庫中的行,如果沒有就插入新行。
在進行下一步之前先把工程重建以下,否則的話下面的步驟會失敗。
在Package Manager控制台視窗中,輸入命令“add-migration Initial”來建立初始遷移。名字"Initial"只不過是隨其起的,用來為建立的遷移檔案命名。
Codi First Migrations在Migrations檔案夾下建立了另外一個類檔案(用{時間戳記}_Initial.cs命名),這個類包含了建立資料庫模式的代碼。這個潛入檔案用一個時間戳記做首碼便於排序。查看一下{時間戳記}_Initial.cs檔案裡的內容,包含了為Movie DB建立Movies表的指令。當我們使用下面的指令建立資料庫時,{時間戳記}_Initial.cs檔案裡的代碼將會被執行來建立資料庫模式,然後調用Seed方法來為資料庫添加測試資料。
如果你看到了一個錯誤說資料庫表已經存在無法被重複建立,這很可能是因為你在刪除資料庫之後,執行update-database命令之前運行了程式。在這種情況下,重新刪除Movies.mdf檔案再次執行update-database命令。如果仍然報錯的話,刪除migrations檔案夾和檔案夾下的檔案,重複文章開始的步驟。
運行應用程式導航到/Movies,seed方法裡添加的資料會被展示出來。
為Movie實體類添加一個Rating屬性
讓我們開始為Movie類添加一個Rating屬性。開啟Models\Movie.cs檔案並且添加Rating屬性。現在完整的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
; } }
重建以下項目。
現在我們已經更新了Model類,接下來更新以下\Views\Movies\Index.cshtml和\Views\Movies\Create.cshtml視圖模板來顯示這個新添加的屬性。
開啟、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")</p><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檔案在表單的結尾附近添加如下的標籤,當一部新電影被建立時會渲染一個文字框出來這樣使用者可以指定一個分級。
<div class="editor-label"> @Html.LabelFor(model => model.Rating)</div><div class="editor-field"> @Html.EditorFor(model => model.Rating) @Html.ValidationMessageFor(model => model.Rating)</div>
現在運行程式並且導航到/Movies,你會看到下面的錯誤:
看到這個錯誤的原因是Movie實體類和資料庫中Movie表的結構不一致了,資料庫中沒有Rating欄位。
有幾種方法可以解決這個問題:
1.讓Entity Framework自動刪除並重新基於新的model類建立資料庫。當在一個測試庫上做開發的時候非常方便,可以讓你非常快速的同步資料庫和model。缺點就是你將會失去資料庫中的所有資料——所以在生產環境的資料庫中你是不可能使用這種方法的!使用一個初始化器來自動填滿測試資料(前面提到的seed方法)在開發時非常有成效。關於Entity Framework資料庫初始化器的更多內容,參見Tom Dykstra的 ASP.NET MVC/Entity Framework tutorial.
2.顯示更改現存的資料庫表結構來和model保持一致。有點是所有的資料都不會丟失,你可以手動修改或者寫一些指令碼來修改。
3.使用Code First Migrations來更新資料庫結構。
在這篇文章中,我們使用Code First Migrations。
修改Seed方法來為新列提供一個值。開啟Migrations\Configuration.cs檔案為每一個Movie對象添加Rating屬性的賦值。
new Movie { Title = "When Harry Met Sally", ReleaseDate = DateTime.Parse("1989-1-11"), Genre = "Romantic Comedy", Rating = "G", Price = 7.99M },
重建項目,然後開啟Package Manager Console視窗輸入下面的命令:add-migration AddRatingMig
add-migration命令告訴migration架構核查movie實體類和現有的Movie資料庫,並且建立必須的代碼來是資料庫結構與model一致。AddRatingMig是隨意起的名字來為migration檔案命名,使用一個有意義的名字是非常有協助的。
當這個命令實行完成後,開啟定義新的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"); }}
重建項目,在Package Manager Console視窗中輸入"update-database"命令。
下面是Package Manager Console視窗的輸出(在AddRatingMig前面的時間戳記會不同)
重新運行程式,導航到/Movies,我們將會看到新添加的欄位。
點擊Create New連結來添加一部新電影,注意我們可以為Rating欄位填一個值
點擊Create按鈕,新的Movies列表如下
同樣也可以再Edit,Details和SearchIndex的視圖模板中添加Rating欄位。
這時候如果在Package Manager Console視窗中再次輸入update-database命令的話,什麼都不會發生,因為資料庫表結構和model是一致的。
在這一小節我們知道了如何修改model對象並且是資料庫和model保持一致。我們也介紹了一種為建立的資料庫添加一些測試資料的方法。接下來,讓我們看一看如何為model類添加豐富的邏輯驗證規則。