[.NET領域驅動設計實戰系列]專題一:前期準備之EF CodeFirst

出處:http://www.cnblogs.com/zhili/p/EFCodeFirst.html


一、前言

  從去年已經接觸領域驅動設計(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實體類,具體類的實現代碼如下:

複製代碼

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的實現如下:

using System.Data.Entity;public class BookDbContext :DbContext
    {        public DbSet<Book> Books { get; set; }
    }

  BookDbContext代表EF中Book在數據庫中的上下文對象,通過DbSet<Book>使實體類與數據庫關聯起來。Books屬性表示數據中的數據集實體,用來處理數據的存取與更新。

  步驟三:添加數據庫連接

  在Web.config文件中,修改數據庫連接字符串的配置,這裏將數據庫連接的name屬性設置爲BookDbContext,後面代碼將會使用到該名字,並根據連接創建相應的數據庫。

 <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的代碼爲如下所示:

複製代碼

 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代碼爲如下所示的代碼:

複製代碼

@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類的屬性,生成對應的標籤名和編輯框,我們修改標籤使其顯示中文,修改會的代碼如下所示:

複製代碼

@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>@section 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的驗證信息。

  編譯項目,在瀏覽器中輸入http://localhost:2574/Book,然後點擊”添加圖書“來查看添加圖書界面,運行結果如下圖所示:

  步驟六:添加Create的Postback方法

  添加Create視圖後,此時僅僅是把添加圖書界面顯示出來,並不能實際的完成圖書的添加操作,因爲我們還沒有添加按鈕的處理方法,沒有實際的處理添加事件,爲了完成數據的添加,在BookController類添加一個Create的Postback方法,完整的BookController代碼如下所示:

複製代碼

 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類爲如下:

複製代碼

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節點:

複製代碼

<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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章