一、前言
從去年已經接觸領域驅動設計(Domain-Driven Design)了,當時就想自己搭建一個DDD框架,所以當時看了很多DDD方面的書,例如領域驅動模式與實戰,領域驅動設計:軟體核心複雜性應對之道和領域驅動設計C# 2008實現等書,由於當時只是看看而已,並沒有在自己程式碼中進行實現,只是初步瞭解一些DDD分層的思想和一些基本概念,例如實體,聚合根、倉儲等概念,今年有機會可以去試試面試一個架構崗位的時候,深受打擊,當面試官問起是否在專案中使用過DDD思想來架構專案時,我說沒有,只是瞭解它的一些基本概念。
回來之後,便重新開始學習DDD,因為我發現做成功面試一個架構師,則必須有自己的一個框架來代表自己的知識體系,而不是要你明白這個基本概念。此時學習便決定一步步來搭建一個DDD框架。但是這次的過程並不是像dax.net那樣,一開始就去搭建框架,然後再用一個實際的專案來做演示框架的使用。因為我覺得這樣對於一些初學者來學習的話,難度比較大,因為剛開始寫框架根本看到什麼,而且看dax.net的Apworks框架很多程式碼也不明白他為什麼這麼寫的,從框架程式碼並不能看出作者怎麼一步步搭建框架的,讀者只能一下子看到整個成型的框架,對於剛接觸DDD的朋友難度非常大,以至於學習了一段時間的DDD之後,就放棄了。
這個感覺本人學習過程中深有體會。所以本系列將直接把DDD的思想應用到一個例項專案中,完全例項專案後,再從中抽取一個DDD框架出來,並且會一步步介紹如何將DDD的思想應用到一個實際專案中(dax.net中ByteartRetail專案也是直接給出一個完整的DDD演示專案的,並沒有記錄搭建過程,同樣對於讀者學習難度很大,因為一下子來吸收整個專案的知識,接受不了,讀者自然就心灰意冷,也就沒有繼續學習DDD的動力了)。本文並沒有開始介紹DDD專案的實際實現,而是一個前期準備工作,因為DDD專案中一般會使用的實體框架來完成,作為.NET陣營的人,自然首先會使EntityFramework。下面就具體介紹下EF中code First的實現,因為在後面的DDD專案實現中會使用到EF的CodeFirst。
二、EF CodeFirst的實現步驟
因為我之前沒怎麼接觸EF的CodeFirst實現,所以在看dax.net的ByteartRetail專案的時候,對EF倉儲的實現有疑惑,所以去查閱相關EF的教程發現,原來應用了EF中的CodeFirst。所以把過程記錄下來。下面就具體介紹下使用EF CodeFirst的具體實現步驟。
步驟一:建立一個Asp.net MVC 4 Web專案,建立成功後,再新增一個Model類
CodeFirst自然是先寫實體類了,這裡演示的是一個Book實體類,具體類的實現程式碼如下:
1 2 3 4 5 6 7 8 9 |
public class Book { public int BookId { get; set; } public string BookName { get; set; } public string Author { get; set; } public string Publisher { get; set; } public decimal Price { get; set; } public string Remark { get; set; } } |
將使用這個類表示資料庫中的一個表,每個Book類的例項對應資料庫中的一行,Book類中的每個屬性對映為資料庫中的一列。
步驟二:建立“BookDbContext”的類
使用Nuget安裝Entity Framework,安裝成功後,在Models資料夾下新建一個“BookDbContext”的類,將類派生自“DbContext”類(名稱空間為System.Data.Entity,dll在EntityFramework),具體BookDbContext的實現如下:
1 2 3 4 5 6 |
using System.Data.Entity; public class BookDbContext :DbContext { public DbSet <Books> { get; set; } } |
BookDbContext代表EF中Book在資料庫中的上下文物件,通過DbSet使實體類與資料庫關聯起來。Books屬性表示資料中的資料集實體,用來處理資料的存取與更新。
步驟三:新增資料庫連線
在Web.config檔案中,修改資料庫連線字串的配置,這裡將資料庫連線的name屬性設定為BookDbContext,後面程式碼將會使用到該名字,並根據連線建立相應的資料庫。
1 2 3 |
<connectionStrings> <add name="BookDbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=BookDB;Integrated Security=True;AttachDBFilename=|DataDirectory|\BookDB.mdf" providerName="System.Data.SqlClient"/> </connectionStrings> |
步驟四:為Book建立控制器和Index檢視
首先建立一個控制器:在”Controlllers”上右鍵>新增>控制器,在開啟的新增控制器對話方塊中,將控制器的名稱改為”BookController”,模板選擇”空控制器“。修改BookController的程式碼為如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class BookController : Controller { readonly BookDbContext _db = new BookDbContext(); // // GET: /Book/ public ActionResult Index() { var books = from b in _db.Books where b.Author == "Learninghard" select b; return View(books.ToList()); } public ActionResult Create() { return View(); } } |
在Index方法內右鍵>”新增檢視”,在開啟的”新增檢視“對話方塊,勾選”建立強型別檢視“,在模型類列表中選擇”Book“(如果選擇列表為空,則需要首先編譯下專案),在支架模板列表中選擇”List“,具體如下圖所示:
點選新增按鈕,VS為我們建立了Index.cshtml檔案,修改Index.cshtml程式碼為如下所示的程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
@model IEnumerable<EF_CodeFirstImp.Models.Book> @{ ViewBag.Title = "圖書列表-EF CodeFirstImp"; } <h2>Index</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:2574/Book,得到的執行結果如下:
儘管沒有資料,但EF已經為我們建立了相應的資料庫了。此時在App_Data資料夾下生成了BookDB資料庫,在解決方案點選選擇所有檔案,將BookDB資料庫包括在專案中。
步驟五:新增Create檢視
在BookController中的Create方法右鍵新增檢視來新增Create檢視,此時模型類仍然選擇Book,但支架模板選擇”Create”。新增成功後,VS會在Views/Book目錄下新增一個Create.cshtml檔案,由於這裡選擇了Create支架框架,所以VS會為我們生成一些預設的程式碼。在這個檢視模板中,指定了強型別Book作為它的模型類,VS檢查Book類,並根據Book類的屬性,生成對應的標籤名和編輯框,我們修改標籤使其顯示中文,修改會的程式碼如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
@model EF_CodeFirstImp.Models.Book @{ ViewBag.Title = "新增圖書"; } <h2>新增圖書</h2> @using (Html.BeginForm()) { @Html.AntiForgeryToken() @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> <a href='http://www.jobbole.com/members/SECTION'>@section</a> Scripts { @Scripts.Render("~/bundles/jqueryval") } |
分析上面的程式碼:
- @model EF_CodeFirstImp.Models.Book:指定該檢視模板中的“模型”強型別化是一個Book類。
- @using (Html.BeginForm()){ }:建立一個Form表單,在表單中包含了對於Book類所生成的對應欄位。
- @Html.EditorFor(model => model.BookName):根據模型生成模型中BookName的編輯控制元件(生成一個Input元素)
- @Html.ValidationMessageFor(model => model.BookName):根據模型生成模型中BookName的驗證資訊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public class BookController : Controller { readonly BookDbContext _db = new BookDbContext(); // // GET: /Book/ public ActionResult Index() { var books = from b in _db.Books where b.Author == "Learninghard" 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); } } |
這時,我們在新增圖書介面中輸入資料,並點選”新增“按鈕時,資料庫中就會新增一行記錄。開啟資料庫,我們將看到如下截圖的資料:
步驟七:設定檢視模型的資料驗證
我們可以在模型類中顯式地追加一個驗證規則,使得對輸入資料進行驗證。修改之前的Book類為如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
using System.ComponentModel.DataAnnotations; //需要額外新增該名稱空間 public class Book { public int BookId { get; set; } [Required(ErrorMessage = "必須輸入圖書名稱")] [StringLength(maximumLength: 100, MinimumLength = 1, ErrorMessage = "最多允許輸入100個字元")] public string BookName { get; set; } [Required(ErrorMessage ="必須輸入作者名稱")] public string Author { get; set; } [Required(ErrorMessage ="必須輸入出版社名稱" )] public string Publisher { get; set; } public decimal Price { get; set; } public string Remark { get; set; } } |
此時重新執行,並開啟新增圖書頁面,當不輸入任何資料的時候,點選”新增“按鈕時,介面會出現一些提示資訊,並阻止我們進行資料的提交操作,具體的結果介面如下所示:
另外,EF建立資料庫除了在第三步中新增連線字串的方式外,還可以定義defaultConnectionFactory中新增parameters節點的方式來完成,dax.net中ByteartRetail專案中就是採用了這種方式。下面註釋掉connectionStrings節點,在defaultConnectionFactory新增如下parameters節點:
1 2 3 4 5 6 7 8 9 10 |
<entityFramework> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" > <parameters> <parameter value="Data Source=(LocalDb)\v11.0;Initial Catalog=BookDB_2;Integrated Security=True;AttachDBFilename=|DataDirectory|\BookDB_2.mdf"/> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework> |
entityFramework節點是使用Nuget新增Entity Framework後自動新增的節點。下面測試下這種方案是否可以成功生成BookDB_2資料庫呢?
執行專案,在瀏覽器中輸入地址:http://localhost:2574/Book,顯示介面成功後,你將在你的App_Data目錄下看到如下截圖:
從上圖可以發現,這種方式同樣成功生成了資料庫。
三、總結
到這裡,領域驅動設計實戰系列的前期準備就結束了,本文主要介紹瞭如何使用EF CodeFirst的功能自動生成資料庫、以及實體的新增、檢視操作等。這裡也簡單介紹了MVC相關內容。下一專題將介紹如何利用DDD的思想來構建一個簡單的網站,接下來的系列就逐一加入DDD的概念來對該網站進行完善。
本文所有原始碼下載:EFCodeFirstImp.zip