7.1 在我們的Movie模型中添加一個Rating(電影等級)屬性
首先,我們在現存的Movie類中添加一個附加的“Rating”屬性。打開Movie.cs文件,在Movie類中添加一個Rating屬性,如下所示。
- public string Rating { get; set; }
現在完整的Movie類的代碼如代碼清單7-1所示。
代碼清單7-1 完整的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; }
- }
點擊“調試”菜單下的“生成MvcMovie”,重新編譯應用程序。
現在我們已經將我們的模型進行了更新,讓我們同樣地修改我們的Views文件夾下的Movies文件夾下的Index.cshtml文件與Create.cshtml這兩個視圖模板文件,在視圖中添加這個Rating屬性。
首先打開Index.cshtml文件,在內容爲“票價”(對應Price屬性)的<th>元素後面追加“<th>電影等級</th>”列標題(對應Rating屬性)。在顯示Price屬性內容的td元素後面追加一個<td>元素,用來顯示Rating屬性的內容。進行了這兩個修改後的Index.cshtml文件中的主要內容如下所示。
- <table>
- <tr>
- <th></th>
- <th>
- 電影名稱
- </th>
- <th>
- 發行日期
- </th>
- <th>
- 種類
- </th>
- <th>
- 票價
- </th>
- <th>
- 電影等級
- </th>
- </tr>
- @foreach (var item in Model) {
- <tr>
- <td>
- @Html.ActionLink("編輯", "Edit", new { id=item.ID }) |
- @Html.ActionLink("查看明細", "Details", new { id=item.ID }) |
- @Html.ActionLink("刪除", "Delete", new { id=item.ID })
- </td>
- <td>
- @item.Title
- </td>
- <td>
- @String.Format("{0:d}", item.ReleaseDate)
- </td>
- <td>
- @item.Genre
- </td>
- <td>
- @String.Format("{0:c2}", item.Price)
- </td>
- <td>
- @item.Rating
- </td>
- </tr>
- }
- </table>
接下來打開Create.cshtml文件,在表單底部追加如下所示的標籤。它將顯示爲一個文本框,用來輸入Rating屬性的內容。
- <div class="editor-label">
- 電影等級
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Rating)
- @Html.ValidationMessageFor(model => model.Rating)
- </div>
7.2 維護模型與數據庫結構之間的差別
現在我們已經將應用程序修改完畢,在Movie模型中添加了一個Rating屬性。
現在讓我們重新運行應用程序,打開“http://localhost:xx/Movies”這個URL地址,這時,瀏覽器會顯示一個應用程序出錯畫面。如圖7-1所示。
圖7-1 應用程序出錯畫面
導致這個問題發生的原因是因爲在我們的應用程序中,更新後的Movie模型類與我們實際連接的數據庫的結構並不統一(Movies數據表中並沒有Rating列)。
在默認情況下,當你使用EF code-first自動創建數據庫時(就好像本教程中前面所介紹過的那樣),EF code-first會自動在數據庫中追加數據表來使得數據庫的結構與它自動生成的模型類保持同步。如果不同步,EF將會拋出一個錯誤。這使得在開發程序時對於錯誤的跟蹤會變得更加容易,否則你只能在運行時發現這個錯誤。同步檢查特性正是引起以上顯示的錯誤的原因。
有兩種方法可以解決這個錯誤:
- 讓Entity Framework自動刪除當前數據庫,並在新的模型類的基礎上重新創建該數據庫。這種方法在使用一個測試數據庫時對於開發來說是十分方便的,因爲它允許你快速地同步修改模型與數據庫。但缺點是你將丟失現存庫中的數據(所以請不要將這個方法使用在實際使用中的數據庫上)。
- 修改數據庫中的數據表的結構來使之與模型相匹配。這個方法的好處是可以讓你保留表中的數據。你可以手工實現這一操作,或創建一個數據表修改腳本來實現這一操作。
在本教程中,我們使用第一種方法,在任何模型發生了改變的情況下讓EF code-first自動重建數據庫。
7.3 當模型改變時自動重建數據庫
現在我們來修改我們的應用程序,使得我們的應用程序中如果任何模型發生了改變,都將自動刪除與重建當前模型所使用的數據庫。(請使用一個開發測試用的數據庫來實踐這一操作。用在實際使用的數據庫中將造成庫中數據的丟失)。
在解決方案資源管理器中,鼠標右擊Modes文件夾,選擇“添加”,然後點擊“類”,如圖7-2所示。
圖7-2 添加一個新類
在“添加新項”對話框中,將類名定義爲“MovieIntializer”,然後點擊添加按鈕添加該類。書寫該類的代碼如代碼清單7-2所示。
代碼清單7-2 MovieIntializer類的完整代碼
- using System;
- using System.Collections.Generic;
- using System.Data.Entity.Database;
- namespace MvcMovie.Models
- {
- public class MovieIntializer :
- DropCreateDatabaseIfModelChanges<MovieDBContext>
- {
- protected override void Seed(MovieDBContext context)
- {
- var movies = new List<Movie> {
- new Movie { Title = "非誠勿擾 2",
- ReleaseDate=DateTime.Parse("2011-1-11"),
- Genre="愛情",
- Rating="R",
- Price=7.00M},
- new Movie { Title = "趙氏孤兒",
- ReleaseDate=DateTime.Parse("2011-2-23"),
- Genre="歷史",
- Rating="R",
- Price=9.00M},
- };
- movies.ForEach(d => context.Movies.Add(d));
- }
- }
- }
使用這個MovieInitializer類之後,一旦我們的模型類發生改變後,我們的模型類所映射的數據庫都會被自動重建。代碼中使用了Seed方法來指定任何重建數據庫的時候想要默認追加到某張數據表中的數據。這爲將一些示例數據添加到數據表中的操作提供了一個有用的方法,而不需要重建了數據庫之後再手工到數據表中添加示例數據。
現在我們已經定義好了我們的MovieInitializer類,接下來我們想在整個工程中使用這個類,這樣每次在運行我們的應用程序的時候會自動檢查當前我們的模型類結構是否與數據庫結構不一致,如果不一致的時候就自動重建該數據庫,並且追加MovieInitializer類中所指定的默認數據。
打開我們的MvcMovies工程的根目錄下的Global.asax文件,如圖7-3所示。
圖7-3 Global.asax文件
Global.asax文件中定義了當前工程所使用到的Application(應用程序)主類,包含了一個Application_Start()事件處理器,當第一次運行我們的應用程序時會觸發這個事件。
讓我們在文件頭部追加兩個有用的聲明。第一個聲明引用Entity Framework命名空間,第二個聲明引用我們的MovieInitializer類所存在的命名空間。這兩句聲明的代碼如下所示。
- using System.Data.Entity.Database; // DbDatabase.SetInitialize
- using MvcMovie.Models; // MovieInitializer
接下來尋找到Application_Start方法,在該方法的開頭追加一個DbDatabase.SetInitializer()方法,代碼如下所示。
- protected void Application_Start()
- {
- DbDatabase.SetInitializer<MovieDBContext>(new MovieInitializer());
- AreaRegistration.RegisterAllAreas();
- RegisterGlobalFilters(GlobalFilters.Filters);
- RegisterRoutes(RouteTable.Routes);
- }
我們追加的DbDatabase.SetInitializer方法將會在實際使用的數據庫中的結構與我們的Movie模型類所映射的數據庫結構不匹配時自動重建該數據庫,並且自動添加MovieInitializer類中所指定的默認數據。
關閉Global.asax文件。
重新運行我們的應用程序,並在瀏覽器中輸入“http://localhost:xx/Movies”。當我們的應用程序啓動的時候,會自動覺察出我們的模型類結構與數據庫的結構不再匹配,於是重建該數據庫使之相匹配,並且自動追加默認數據,瀏覽器中顯示結果如圖7-4所示。
圖7-4 修改了模型類結構並追加默認數據後瀏覽器中的顯示結果
點擊追加按鈕進行數據的追加,如圖7-5所示。
圖7-5 追加數據
點擊追加按鈕後,新追加的包括Rating(電影等級)字段的數據能正常追加並在電影清單畫面中正常顯示,如圖7-6所示。
圖7-6 包括Rating(電影等級)字段的數據能被正常追加與顯示
7.4 修正票價字段的精度
在第六節追加數據的時候我們遺留下來一個問題,就是我們在追加數據的時候,票價(Price)字段中輸入的是9.99元,但是電影清單顯示畫面中該數據的票價字段顯示爲10元,這是爲什麼?
這個問題發生的原因是因爲當EF code-first在創建數據表的時候,如果字段爲Decimal類型,則使用默認的精度(18:0),從而使得9.99元被四捨五入成爲10元。現在我們想要將這個默認的精度修改爲(18:2),從而使得數據表中的票價字段能夠存儲小數點後的兩位數字。可喜的是EF code-first允許你很容易地重載這個定義模型如何向數據庫中存取數據的映射規則。你可以利用這個重載機制來重載EF code-first中默認的類型定義以及數據表的繼承規則,然後在這個基礎上進行數據的存取。
爲了改變我們的票價(Price)字段在數據表中的精度,打開Models文件夾下的Movie.cs文件。追加一句引用System.Data.Entity.ModelConfiguration的語句,代碼如下所示。
- using System.Data.Entity.ModelConfiguration;
在我們現在的MovieDBContext類中重載OnModelCreating方法,代碼如下所示。
- public class MovieDBContext : DbContext
- {
- public DbSet<Movie> Movies { get; set; }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity<Movie>().Property(p => p.Price).HasPrecision(18, 2);
- }
- }
OnModelCreating方法可以被用來重載與定製規定我們的模型類如何與我們的數據表進行映射的映射規則。代碼中使用了EF的ModelBuilder API來定義我們的Movie對象的票價(Price)字段的精度爲準確到小數點後兩位。
現在完整的Movie.cs中的代碼如代碼清單7-3所示。
代碼清單7-3 完整的Movie.cs中的代碼
- using System;
- using System.Data.Entity;
- using System.Data.Entity.ModelConfiguration;
- namespace MvcMovie.Models
- {
- 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; }
- }
- public class MovieDBContext : DbContext
- {
- public DbSet<Movie> Movies { get; set; }
- protected override void OnModelCreating(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity<Movie>().Property(p =>
- p.Price).HasPrecision(18, 2);
- }
- }
- }
現在讓我們重新運行我們的應用程序,並且在瀏覽器中輸入“http://localhost:xx/Movies”。當應用程序啓動的時候,EF code-first將會再次察覺我們的模型類中的結構與數據表的結構不再匹配,然後自動重建數據表與新的模型類的結構進行匹配(具有新的票價(Price)字段的精度)。
重新創建一個新的電影(Movie)數據,在票價(Price)字段中輸入9.99。請注意數據表中保存後顯示在電影清單畫面中的該條數據的票價(Price)字段中現在也顯示爲9.99了,如圖7-7中所示。
圖7-7 修改精度後電影清單畫面中的顯示結果
在本節中我們介紹瞭如何快速調整你的模型對象,同時在改變模型類的結構時自動將數據表中的結構保持同步。我們也介紹瞭如何預先在自動重建的數據表中追加默認數據,它使得你可以快速在數據表中追加一些測試數據。在下一節中,我們將介紹如何在我們的模型類中加入我們自定義的數據有效性校驗規則,從而強制實現一些業務規則。