8.1 DRY原則
在ASP.NET MVC中,有一條作爲核心的原則,就是DRY(“Don’t Repeat Yourself,中文意思爲:不要讓開發者重複做同樣的事情)原則。ASP.NET MVC提倡讓開發者“一處定義、處處可用”。這樣可以減少開發者的代碼編寫量,同時也更加便於代碼的維護。
ASP.NET MVC與EF code-first提供的默認驗證規則就是一個實現DRY原則的很好的例子。你也可以在模型類中顯式地追加一個驗證規則,然後在整個應用程序中都使用這個驗證規則。
現在讓我們來看一下怎樣在我們的應用程序中追加一些驗證規則。
8.2 在Movie模型中追加驗證規則
首先,讓我們在Movie類中追加一些驗證規則。
打開Movie.cs文件,在文件的頭部追加一條引用System.ComponentModel.DataAnnotations命名空間的using語句,代碼如下所示。
- using System.ComponentModel.DataAnnotations;
這個System.ComponentModel.DataAnnotations命名空間是.NET Framework中的一個命名空間。它提供了很多內建的驗證規則,你可以對任何類或屬性顯式指定這些驗證規則。
現在讓我們來修改Movie類,增加一些內建的Required(必須輸入),StringLength(輸入字符長度)與Range(輸入範圍)驗證規則,代碼如代碼清單8-1所示。
代碼清單8-1 在Movie類中追加內建的驗證規則
- public class Movie
- {
- public int ID { get; set; }
- [Required(ErrorMessage = "必須輸入標題")]
- public string Title { get; set; }
- [Required(ErrorMessage = "必須輸入發行日期")]
- public DateTime ReleaseDate { get; set; }
- [Required(ErrorMessage = "必須指定種類")]
- public string Genre { get; set; }
- [Required(ErrorMessage = "必須輸入票價")]
- [Range(1, 100, ErrorMessage = "票價必須在1元到100元之間")]
- public decimal Price { get; set; }
- [StringLength(5,ErrorMessage = "最多允許輸入五個字符")]
- public string Rating { get; set; }
- }
上述這些驗證屬性指定了我們想要強加給模型中各屬性的驗證規則。Required屬性表示必須要指定一個屬性值,在上例中,一個有效的電影信息必須含有標題,發行日期,種類與票價信息。Range屬性表示屬性值必須在一段範圍之間。StringLength屬性表示一個字符串屬性的最大長度或最短長度。
EF code-first在將一條數據保存到數據庫中之前首先使用你對模型類指定的驗證規則來對這條數據進行有效性檢查。例如,在以下代碼中,當程序調用SaveChanges方法時將拋出一個異常(也稱例外),因爲數據並不滿足Movie屬性的必須輸入條件,同時票價屬性的值爲0,不在指定的允許範圍內(1-100)。
- MovieDBContext db = new MovieDBContext();
- Movie movie = new Movie();
- movie.Title = "大笑江湖;
- movie.Price = 0.0M;
- db.Movies.Add(movie);
- db.SaveChanges(); // 這裏將拋出一個校驗異常
通過Entity Framework來自動實現驗證規則檢查可以讓我們的應用程序變得更強健。它也確保我們不會由於忘了實施數據驗證而使得一些無效數據保存到數據庫中。
代碼清單8-2爲現在Movie.cs文件中的完整代碼。
代碼清單8-2 Movie.cs文件中的完整代碼
- using System.Data.Entity;
- using System.Data.Entity.ModelConfiguration;
- using System.ComponentModel.DataAnnotations;
- namespace MvcMovie.Models
- {
- public class Movie
- {
- public int ID { get; set; }
- [Required(ErrorMessage = "必須輸入標題")]
- public string Title { get; set; }
- [Required(ErrorMessage = "必須輸入發行日期")]
- public DateTime ReleaseDate { get; set; }
- [Required(ErrorMessage = "必須指定種類")]
- public string Genre { get; set; }
- [Required(ErrorMessage = "必須輸入票價")]
- [Range(1, 100, ErrorMessage = "票價必須在1元到100元之間")]
- public decimal Price { get; set; }
- [StringLength(5,ErrorMessage = "最多允許輸入五個字符")]
- 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);
- }
- }
- }
8.3 ASP.NET MVC中的驗證錯誤UI(用戶界面)
現在讓我們運行我們的應用程序,並在地址欄中輸入“http://localhost:xx/Movies”。
在電影清單畫面中點擊追加按鈕打開追加電影畫面。在該畫面中的表單中填入一些無效的屬性值,然後點擊追加按鈕。如圖8-1所示。
圖8-1 ASP.NET MVC中的驗證錯誤UI
請注意表單自動使用了一個背景顏色來高亮顯示包含了無效數據的文本框,並且在每個文本框的旁邊顯示驗證錯誤信息。使用的錯誤信息文字正是我們在前面代碼中所指定的驗證錯誤的錯誤信息文字。這個驗證錯誤既可以由客戶端引發(使用JavaScript腳本),也可以由服務器端引發(當用戶禁止使用JavaScript腳本時)。
這種處理方法是非常不錯的,因爲我們不再需要爲了顯示錯誤信息文字而在MoviesController類或Create.cshtml視圖文件中書寫不必要的代碼。我們之前創建的控制器與視圖將自動實施驗證規則與顯示驗證錯誤信息文字。
8.4 在Create視圖(追加電影視圖)與Create方法內部是如何實現驗證的
也許有的讀者會問,既然我們沒有追加任何顯示錯誤信息提示的代碼,那麼我們的控制器或視圖內部是如何生成這個顯示錯誤信息提示的畫面的。首先我們將MovieController類中的代碼顯示如下,在我們在Movie類中追加了驗證規則後,我們並沒有修改這個類中的任何代碼。
- //
- // GET: /Movies/Create
- public ActionResult Create()
- {
- return View();
- }
- //
- // POST: /Movies/Create
- [HttpPost]
- public ActionResult Create(Movie newMovie)
- {
- if (ModelState.IsValid)
- {
- db.Movies.Add(newMovie);
- db.SaveChanges();
- return RedirectToAction("Index");
- }
- else
- {
- return View(newMovie);
- }
- }
第一個方法返回追加電影視圖。在第二個方法中對追加電影視圖中的表單的提交進行處理。該方法中的ModelState.IsValid屬性用來判斷是否提交的電影數據中包含有任何沒有通過數據驗證的無效數據。如果存在無效數據,Create方法重新返回追加電影視圖。如果數據全部有效,則將該條數據保存到數據庫中。
我們之前創建的使用支架模板的Create.cshtml視圖模板中的代碼顯示如下,在首次打開追加電影視圖與數據沒有通過驗證時,Create方法中返回的視圖都是使用的這個視圖模板。
- @model MvcMovie.Models.Movie
- @{
- ViewBag.Title = "追加電影信息";
- }
- <h2>追加電影信息</h2>
- <script src="@Url.Content("~/Scripts/jquery.validate.min.js")"
- type="text/javascript"></script>
- <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"
- type="text/javascript"></script>
- @using (Html.BeginForm()) {
- @Html.ValidationSummary(true)
- <fieldset>
- <legend>電影</legend>
- <div class="editor-label">
- 標題
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Title)
- @Html.ValidationMessageFor(model => model.Title)
- </div>
- <div class="editor-label">
- 發行日期
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.ReleaseDate)
- @Html.ValidationMessageFor(model => model.ReleaseDate)
- </div>
- <div class="editor-label">
- 種類
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Genre)
- @Html.ValidationMessageFor(model => model.Genre)
- </div>
- <div class="editor-label">
- 票價
- </div>
- <div class="editor-field">
- @Html.EditorFor(model => model.Price)
- @Html.ValidationMessageFor(model => model.Price)
- </div>
- <p>
- <input type="submit" value="追加" />
- </p>
- </fieldset>
- }
- <div>
- @Html.ActionLink("返回電影列表", "Index")
- </div>
請注意在這段代碼中使用了許多Html.EditorFor幫助器來爲Movie類的每個屬性輸出一個輸入文本框。在每個Html.EditorFor幫助器之後緊跟着一個Html.ValidationMessageFor幫助器。這兩個幫助器將與從控制器傳入的模型類的對象實例(在本示例中爲Movie對象的一個實例)結合起來,自動尋找指定給模型的各個驗證屬性,然後顯示對應的驗證錯誤信息。
這種驗證體制的好處是在於控制器和Create視圖(追加電影視圖)事先都即不知道實際指定的驗證規則,也不知道將會顯示什麼驗證錯誤信息。驗證規則和錯誤信息只在Movie類中被指定。
如果我們之後想要改變驗證規則,我們也只要在一處地方進行改變就可以了。我們不用擔心整個應用程序中存在驗證規則不統一的問題,所有的驗證規則都可以集中在一處地方進行指定,然後在整個應用程序中使用這些驗證規則。這將使我們的代碼更加清晰明確,更加具有可讀性、可維護性與可移植性。這將意味着我們的代碼是真正符合DRY原則(一處指定,到處可用)的。
在下一節中,作爲結尾部分,我們將介紹如何修改與刪除數據,同時介紹如何顯示一條數據的細節信息。