MVC3教程之實體模型和EF CodeFirst
- 2019 年 10 月 7 日
- 筆記
在本節中,我們將使用Entity Framework 數據訪問技術來定義這些模型類,並對這些類來進行操作。EF支援一個被稱之為「code-first」的開發範例。Code-first允許你通過書寫一些簡單的類來創建模型對象,而不用關心這些類的持久化。你可以通過訪問這些類的方式來訪問資料庫,這是一種非常方便快捷的開發模式。
1.添加一個Model
添加Model和添加普通類的操作是一樣的,默認的約定是將它放在Models文件夾中。我們在Models文件夾上面點擊右鍵,選擇「添加」>「類」,在打開的對話框中輸入類名「Book」,點擊「添加」按鈕。編輯器會為我們打開Book類,我們對這個類進行如下修改:
using System; namespace MvcHelloworld.Models { publicclass Book { publicint BookID { get; set; } publicstring BookName { get; set; } publicstring Author { get; set; } publicstring Publisher { get; set; } publicdecimal Price { get; set; } publicstring Remark { get; set; } } }
我們將使用這個類來表示資料庫中的記錄。每一個Book類的實例對應資料庫中的一行,Book類中的每一個屬性被映射到資料庫中的一列。
2.添加資料庫上下文
在Models文件夾下新建一個名為「BookDbContext」的類,編輯這個類,將該類派生自「DbContext」類,編輯後的程式碼如下:
using System; using System.Data.Entity; namespace MvcHelloworld.Models { publicclass BookDbContext : DbContext { public DbSet<Book> Books { get; set; } } }
BookDbContext代表EF中Book在資料庫中的上下文對象,通過DbSet<Book>使實體類與資料庫關聯起來。Books屬性表示資料庫中的數據集實體,用來處理數據的存取與更新。BookDbContext派生自DbContext,需要添加System.Data.Entity的引用。
3.添加資料庫連接
由於我們創建的是空的Mvc項目,所以在Web.config文件中,不包含任何的資料庫連接字元串,我們打開Web.config文件,為它添加一個資料庫連接字元串的配置:
<connectionStrings> <add name="BookDbContext" connectionString=" Data Source=.SQLEXPRESS;Initial Catalog=db_book;Persist Security Info=True;Integrated Security=SSPI;" providerName="System.Data.SqlClient"/> </connectionStrings>
我們將資料庫連接的name屬性設置為「BookDbContext」,這個連接會被BookDbContext類使用,並根據連接創建相應的資料庫。
4.為Book創建控制器和Index視圖
按照第一節中的步驟,我們為Book模型創建一個控制器:在文件夾「Controllers」上面點擊右鍵 > 「添加」 > 「控制器」,在打開的添加控制器對話框中,將控制器的名稱修改為「BookController」,基架選擇中的模板選擇「空控制器」,如下圖:

點擊「添加」按鈕後,VS會添加一個BookController的文件,該文件處於打開狀態。編輯Index方法的程式碼,查找作者為Tom的圖書:
public ActionResult Index() { var books = from b in db.Books where b.Author =="Tom" select b; return View(books.ToList()); }
在這段程式碼中,db是類BookDbContext的一個實例,我們在Controller類中定義如下:BookDbContext db = new BookDbContext();
這是一個簡單的Linq查詢,在對資料庫進行操作時,EF會檢查當前的數據連接指定的資料庫是否被創建,如果沒有則有EF負責根據實體模型類創建資料庫、數據表;如果存在,EF會將查詢條件添加到Sql查詢語句,再將Sql語句發送到資料庫進行數據讀取。在完成數據讀取後,將數據轉換為實體對象集合。EF對資料庫的操作大致如此。
在Index方法內點擊右鍵 > 「添加視圖」,在打開的「添加視圖」對話框,勾選「創建強類型視圖」,在模型類列表中選擇「Book(MvcHelloworld.Models)」,在支架模板列表中選擇「List」,如下圖:

點擊「添加」按鈕,VS為我們在Views文件夾下創建了「Book」文件夾,並在Book文件夾中添加了文件「Index.cshtml」。
Index.cshtml是我們的視圖頁面,我們可以把它看做一個模板,將我們的數據按照模板的格式進行輸出。在這個模板中,我們使用了Razor視圖引擎,在Razor中,我們可以使用@model 用來指定傳到視圖的 Model 類型,訪問傳入視圖的數據內容。我們簡單的修改程式碼,如果你了解HTML,這將是很簡單的事情:
@model IEnumerable<MvcHelloworld.Models.Book> @{ ViewBag.Title = "圖書列表 - MvcBook"; } <h2>圖書列表</h2> <p> @Html.ActionLink("增加圖書", "Create") </p> <table> <tr> <th> 圖書名稱 </th> <th> 作者 </th> <th> 出版社 </th> <th> 價格 </th> <th> 備註 </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.BookName) </td> <td> @Html.DisplayFor(modelItem => item.Author) </td> <td> @Html.DisplayFor(modelItem => item.Publisher) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.DisplayFor(modelItem => item.Remark) </td> <td> @Html.ActionLink("編輯", "Edit", new { id=item.BookID }) | @Html.ActionLink("查看", "Details", new { id=item.BookID }) | @Html.ActionLink("刪除", "Delete", new { id=item.BookID }) </td> </tr> } </table>
編譯並運行程式,在瀏覽器中輸入地址:http://localhost:xxx/Book,得到的運行結果如下:

儘管沒有數據,但EF已經為我們創建了相應的資料庫。
5.增加Create視圖
「增加圖書」連接需要我們有一個Create控制器和與之對應的視圖。打開BookController文件,添加一個Create方法,程式碼如下:
public ActionResult Create() { return View(); }
這個方法返回一個視圖,該視圖中包含了用戶要輸入的表單。現在我們來實現這個Create視圖,我們將在這個視圖中向用戶顯示追加數據時所需要用到的表單。在Create方法中點擊滑鼠右鍵,並點擊上下文菜單中的「添加視圖」。在「添加視圖」對話框中勾選「創建強類型視圖」,在模型類列表中選擇「Book(MvcHelloworld.Models)」,在支架模板列表中選擇「Create」,如下圖:

點擊「添加」按鈕,VS會在Views/Book目錄下添加一個Create.cshtml文件,由於我們選擇了Create支架模板,所以在VS為我們生成了一些默認的程式碼。在這個視圖模板中,我們指定了強類型Book作為它的模型類,VS檢查Book類,並根據Book類的屬性,生成了對應的標籤名和編輯框,我們修改標籤名,使它顯示中文,修改後的程式碼如下:
@model MvcHelloworld.Models.Book @{ ViewBag.Title = "新增圖書 - MvcBook"; } <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.BookName) @Html.ValidationMessageFor(model => model.BookName) </div> <div class="editor-label"> 作者 </div> <div class="editor-field"> @Html.EditorFor(model => model.Author) @Html.ValidationMessageFor(model => model.Author) </div> <div class="editor-label"> 出版社 </div> <div class="editor-field"> @Html.EditorFor(model => model.Publisher) @Html.ValidationMessageFor(model => model.Publisher) </div> <div class="editor-label"> 價格 </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <div class="editor-label"> 備註 </div> <div class="editor-field"> @Html.EditorFor(model => model.Remark) @Html.ValidationMessageFor(model => model.Remark) </div> <p> <input type="submit" value="增加"/> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div>
分析這段程式碼:
- @model MvcHelloworld.Models.Book:指定了該視圖模板中的「模型」強類型化成一個Book類。
- @using (Html.BeginForm()){ }:創建一個Form表單,在表單中包含了對於Book類所生成的對應欄位。
- @Html.EditorFor(model => model.BookName):根據模型生成模型中BookName的編輯控制項(生成一個Input元素)
- @Html.ValidationMessageFor(model => model.BookName):根據模型生成模型中BookName的驗證資訊。
編譯項目,在瀏覽器中輸入http://localhost:xxx/Book/Create, 查看新增介面,截圖如下:

6.添加Create的Postback方法
在完成了添加Create視圖後,我們僅是可以將添加介面顯示出來,並不能實際的完成數據的添加,因為我們還沒有增加按鈕的處理方法,沒有實際的處理添加事件。為了能夠完成數據的增加,下面我們來添加一個Create的POSTBack方法,程式碼如下:
[HttpPost] public ActionResult Create(Book book) { if (ModelState.IsValid) { db.Books.Add(book); db.SaveChanges(); return RedirectToAction("Index"); } else return View(book); }
這時,我們在頁面上輸入數據,並點擊「增加」按鈕時,EF就會通過這段程式碼來添加一行資料庫記錄。打開資料庫,我們可以看到如下記錄:

7.設置實體模型的數據驗證
在ASP.NET MVC中,有一條作為核心的原則,就是DRY(「Don』t Repeat Yourself,中文意思為:不要讓開發者重複做同樣的事情,即「一處定義、處處可用」)原則。這樣可以減少開發者的程式碼編寫量,同時也更加便於程式碼的維護。
ASP.NET MVC與EF code-first提供的默認驗證規則就是一個實現DRY原則的很好的例子。你也可以在模型類中顯式地追加一個驗證規則,然後在整個應用程式中都使用這個驗證規則。
打開Book模型文件,添加 System.ComponentModel.DataAnnotations 的引用,並修改實體類的程式碼如下:
publicclass Book { publicint BookID { get; set; } [Required(ErrorMessage="必須輸入圖書名稱")] [StringLength(maximumLength:100, MinimumLength=1, ErrorMessage="最多允許輸入100個字元")] publicstring BookName { get; set; } [Required(ErrorMessage ="必須輸入作者名稱")] publicstring Author { get; set; } [Required(ErrorMessage ="必須輸入出版社")] publicstring Publisher { get; set; } publicdecimal Price { get; set; } publicstring Remark { get; set; } }
將資料庫中之前生成的資料庫db_Book刪除掉,重新生成解決方案,打開新增頁面,不輸入任何數據的時候點擊「增加」按鈕,這個時侯,介面上會出現一些提示資訊,並且阻止了我們進行數據的提交操作。介面如下:

這是一個簡單的驗證設置,通過設置驗證,EF還會在生成的資料庫中添加驗證資訊,例如是否為空、字元串長度等,如果要了解更多EF的功能,請看我的另一篇隨筆:Entity Framework 4.1 Code-First 學習筆記
通過本節的學習,我們可以了解EF CodeFirst功能、MVC實體模型的操作等。對於實體的操作,還有更新、查看和刪除操作,筆者不再一一講解其步驟,只將控制器程式碼貼出,以供朋友們參照。視圖的程式碼可以參考自動生成,稍作修改即可。
BookController程式碼 using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using MvcHelloworld.Models; namespace MvcHelloworld.Controllers { publicclass BookController : Controller { BookDbContext db =new BookDbContext(); public ActionResult Index() { var books = from b in db.Books select b; return View(books.ToList()); } public ActionResult Create() { return View(); } [HttpPost] public ActionResult Create(Book book) { if (ModelState.IsValid) { db.Books.Add(book); db.SaveChanges(); return RedirectToAction("Index"); } else return View(book); } public ActionResult Edit(int id) { Book book = db.Books.Find(id); if (book ==null) return RedirectToAction("Index"); return View(book); } [HttpPost] public ActionResult Edit(Book newBook) { try { Book oldBook = db.Books.Find(newBook.BookID); UpdateModel(oldBook); db.SaveChanges(); return RedirectToAction("Details", new { id = newBook.BookID }); } catch (Exception ex) { ModelState.AddModelError("", "修改失敗,請查看詳細錯誤資訊:"+ ex.Message); } return View(newBook); } public ActionResult Details(int id) { Book book = db.Books.Find(id); if (book ==null) return RedirectToAction("Index"); return View(book); } public ActionResult Delete(int id) { Book book = db.Books.Find(id); if (book ==null) return RedirectToAction("Index"); return View(book); } [HttpPost] public ActionResult Delete(int id, FormCollection collection) { Book book = db.Books.Find(id); db.Books.Remove(book); db.SaveChanges(); return RedirectToAction("Index"); } } }