免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
ASP.NET MVC4 IN ACTION學(xué)習(xí)筆記

ASPNET MVC4 都是Action的控制器(Actions-packed Controllers)

--我沒有自信是否能把本次的任務(wù)完成,但我會(huì)負(fù)責(zé)任的完成

原著:ASP.NET MVC 4 IN ACTION

本人能力有限,盡量將書中的知識濃縮去講,仔細(xì)學(xué)過后,然后你再學(xué)習(xí)其他語言的MVC框架也就大同小異了

本次覆蓋知識點(diǎn):

  1. 1. 怎樣才算一個(gè)控制器(What makes a controller)
  2. 2. 控制器中的成員有什么 (What belongs in a controller)
  3. 3. 手動(dòng)映射視圖模型 (Manually mapping view models )
  4. 4. 驗(yàn)證該用戶的輸入 (Validating user input )
  5. 5. 使用默認(rèn)的單元測試模版(Using the default unit test project )

     在最近兩章里,我們看了一下創(chuàng)建一個(gè)Guestbook應(yīng)用程序基礎(chǔ)知識,也看了幾種不同的把數(shù)據(jù)傳遞給視圖的可以實(shí)現(xiàn)的方式。在本章里,我們將會(huì)完成Guestbook例子,我們只添加一點(diǎn)點(diǎn)細(xì)節(jié)部分。我們將研究什么應(yīng)該是controller中的一部分,什么不應(yīng)該是,然后看一下怎樣手動(dòng)構(gòu)造一個(gè)視圖models,驗(yàn)證用戶的輸入,寫幾個(gè)不會(huì)使用到視圖的控制器操作(action)。這個(gè)將會(huì)讓我們在controller中建造一個(gè)很常用action塊(block),也就是不返回view類型action代碼,我們以前學(xué)的都是返回一個(gè)view(),這里就不是的了。

    我們也將向你簡單地介紹一下單元測試controller中的action,這樣可以確保這些action可以正確的工作。我們將會(huì)使用默認(rèn)的單元測試項(xiàng)目,然后為GuestbookController創(chuàng)建單元測試,測試前面幾章的工作情況。

    在我們學(xué)習(xí)這些新知識之前,我們先快速地概括一下controllers和actions

     茗洋芳竹飛機(jī)票:第一章  第二章

 

4.1 探討Controllers和actions

    備注:controller action指controller中的action(也就是你們經(jīng)常寫的方法而已)

            controller actions指controller中全部actions

            action名字加action表示 某某名字的action,例如有個(gè)叫Index的action,我就會(huì)這樣表示

            Index action.知道了嗎?這方便我寫博客,也更方便理解

            view model指頁面(view)中用到model,我上篇博客說的視圖模型就是view model

            action method指 返回值是ActionResult那個(gè)方法,比如Index那個(gè)action方法

            user interface,用戶接口,你可以意會(huì)理解成頁面,也就是要跟用戶交互的東西,比如我們給用提供一個(gè)頁面,讓用戶錄入數(shù)據(jù),這就是一個(gè)interface,一個(gè)接口,你暫且在這里就是view,就是頁面

            business logic,商業(yè)邏輯,類似于數(shù)據(jù)訪問層中業(yè)務(wù)實(shí)現(xiàn)的邏輯

            domain,你可以理解數(shù)據(jù)訪問層

            domain model,數(shù)據(jù)訪問層要使用到的model

            以后我想直接寫英文了,好方便理解,在上一波博客我是吃過苦了,文章讀起來好拗口

      在第一波里面我們看出來了Controller的重要性,它里面有很多方法(action),這些action都可以通過url實(shí)現(xiàn)觸發(fā)調(diào)用.而這些action就是把model中的數(shù)據(jù)傳到頁面上的中間者,也可以理解為搬運(yùn)工吧,所以理解action是怎樣工作的是很重要的.在下一節(jié),我們主要更詳細(xì)地了解下controller actions是怎樣工作的.

      下面我們看下action的樣子(GuestbookController中的Index action)

     

      它繼承了Controller,也含有默認(rèn)的action,我們也知道所有的控制器必須繼承Controller,其實(shí)框架允許我們只要至少實(shí)現(xiàn)IController否則,它是不能處理Web請求的

      下面我們看看框架是怎么知道哪個(gè)class是個(gè)被當(dāng)做controller來處理,因?yàn)閏ontroller說白了也是一個(gè)類而已.下面讓我們看看

IController

     

4.1.1   IController和Controller 父類(base class)

                  

IController定義了一個(gè)controller最根本的東東---Execute方法接受一個(gè)RequestContext類型的對象

下面是一個(gè)簡單的controller,我們寫一些html放到響應(yīng)流里面(response stream)

我們手動(dòng)實(shí)現(xiàn)一下:

這是一個(gè)實(shí)現(xiàn)了IController的控制器,我們實(shí)現(xiàn)了Execute方法,我們就能直接訪問HttpContext,Request還有Response對象了.這種方式很容易定義但是沒什么用.這樣做,我們就無法直接呈現(xiàn)view.而且我們這樣做---在controller中直接就寫HTML,把邏輯和呈現(xiàn)的內(nèi)容混在一起了,很難受.

好吧,我們先不看框架那些有用的特征,比如說安全性(第8章會(huì)講),model binding(第10章),action results(第16章).

這樣做,我們也喪失了定義action方法的能力----------因?yàn)樗械恼埱蠖急籈xecute處理了

實(shí)際上,你需要去實(shí)現(xiàn)IController也不太可能那樣去做,因?yàn)樗旧頉]什么用(避開框架的主要部分,有一些理由能讓你覺的還是有用的).通常我們會(huì)繼承父類----------ControllerBase和Controller

ControllerBase

ControllerBase類除了包含了我們已經(jīng)看過的一些基本特征以外,它本身就直接實(shí)現(xiàn)了IController.舉個(gè)例子:ControllerBase也包含ViewData屬性(把數(shù)據(jù)出給view的一個(gè)手段).但是ControllerBase仍然還是不怎么有用-----它仍然還是不能通過使用action來呈現(xiàn)view.所以Controller來了

Controller

Controller繼承ControllerBase,所以除了包含一些有意義的東西以外,它還包含了ControllerBase定義的東東(比如ViewData).它包含了ControllerActionInvoker(這是一個(gè)知道怎樣根據(jù)url找到對應(yīng)的方法(method),并執(zhí)行了那個(gè)方法,它定義了一些方法,比如View(可以通過controller action來呈現(xiàn)一個(gè)view))

這是一個(gè)當(dāng)你開始創(chuàng)建好你的controller的時(shí)候會(huì)自動(dòng)繼承的類,所以說直接繼承ControllerBase或者IController都不會(huì)有什么幫助.但是我們知道了在MVC 管道(pipeline)模型中扮演很重要的角色,知道它們的存在還是很有用的.

好了,現(xiàn)在我們已經(jīng)知道了怎樣把一個(gè)類變成一個(gè)Controller.下面我們看看action

 

4.1.2   怎樣才可以成為一個(gè)action method

         在第二章(我的第一波博客,我從第二章開始講的,第一章是介紹,以后我會(huì)按書里面的章節(jié)順序講,還希望諒解)我們  在controller中看到了一些public的action方法(事實(shí)上,決定一個(gè)方法能否成為一個(gè)action,是有條規(guī)律規(guī)則的,講起來還是蠻復(fù)雜的,我們將會(huì)在第16章開始講解).

        平常呢,action method都是返回一個(gè)ActionResult的一個(gè)實(shí)例.舉個(gè)例子,一個(gè)action返回值可以是void類型的,也可以直接輸出HTML(很像我們剛剛的SimpleController)

我們也可以這樣寫,它們結(jié)果是一樣的,返回一個(gè)HTML片段(snippet)

這個(gè)沒有問題,因?yàn)镃ontrollerActionInvoker它保證了action的返回值總是ActionResult.如果action返回的是一個(gè)ActionResult(例如ViewResult),ControllerActionInvoker就會(huì)被調(diào)用.但是如果action返回的一個(gè)不同的類型(就像這里,一個(gè)string),它的返回值是ContentObject對象(也是一個(gè)把要寫入的東西放入響應(yīng)流里面的東東),直接使用ContentResult也是一樣的

這就是一個(gè)很簡單的action了,不需要view就可以直接在瀏覽器中呈現(xiàn)HTML標(biāo)簽了。但是在真實(shí)世界的應(yīng)用程序中通常不會(huì)這樣用的,還是最好通過view把要呈現(xiàn)的頁面和controller分開,這樣做當(dāng)我們改變user interface的時(shí)候,就不用動(dòng)controller中的代碼了

除了呈現(xiàn)標(biāo)簽或者返回一個(gè)view,還有其他幾種類型,舉個(gè)例子,我們可以使用RedirectToRouteResult,讓用戶在瀏覽的時(shí)候跳到其他頁面(我們在第二章(我的第一波博客)中的RedirectToAction方法你已經(jīng)使用過了,還記得嗎?),我們也可以返回JSON(在第七章的AJAX中我們將會(huì)學(xué)到)

我們可以使用NonActionAttribute讓一個(gè)controller中的public的action不是一個(gè)action。

NonActionAttribute是一個(gè)action方法選擇器(action method selector),它可以重寫 匹配一個(gè)方法名到一個(gè)action名的默認(rèn)行為(behavior).NonActionAttribute是最簡單的一個(gè)選擇器,它可以使那些可以通過URL訪問的方法不可以通過這種手段來訪問,其實(shí)在第二章,我們也已經(jīng)看到了一些選擇器,比如HttpPostAttribute(可以確保這個(gè)action可以響應(yīng)HTTP post形式的請求,其他方式都是不可以訪問的)

   提醒

    NonActionAttribute很少使用的,在一個(gè)controller中一個(gè)public的方法你不想它成為action,你最好想一下controller中是不是它最好的地方,如果這個(gè)方法有用,你可能會(huì)寫成private訪問修飾符級別的。如果這個(gè)方法必須public,因?yàn)樾枰獪y試的原因,我覺得應(yīng)該提取成一個(gè)單獨(dú)的類

現(xiàn)在讓我簡單地看一下什么構(gòu)成了action,你將會(huì)看到一些不同的方式,把內(nèi)容放到瀏覽器中顯示。除了view,你也可以直接發(fā)送content(內(nèi)容),或者直接執(zhí)行其他的action(重定向),這些技術(shù)在你的應(yīng)用程序中都會(huì)很有用。

現(xiàn)在讓我們?nèi)タ匆幌耡ction中的邏輯

 

4.2  在action method中有什么

              MVC中最大的兩點(diǎn)就是將各自的職責(zé)分離,user interface 和 邏輯(比如頁面提交數(shù)據(jù)怎么處理)分開,因此整個(gè)程序就更容易維護(hù)(maintain)了。如果你沒有讓你的controller更精巧(lightweight實(shí)際是輕量級的意思),把重點(diǎn)都放在controller里,你就不會(huì)體會(huì)到它的好處了。

             Controller應(yīng)該扮演中間者(coordinator)--它不應(yīng)該包含business logic。反之亦然(vice versa),它可以把view上的表單,用戶的輸入的數(shù)據(jù)(user input),來自view的,封裝成一個(gè)對象,然后我們把這個(gè)對象在domain(實(shí)際business logic代碼寫

在的地方)層中使用,這樣我們數(shù)據(jù)的存儲(chǔ)都可以完成了。

              讓我們看一下通過controller如何把一個(gè)任務(wù)完成--手動(dòng)的映射view models,接受用戶的輸入的數(shù)據(jù)。首先,展現(xiàn)的是如何匹配view models,現(xiàn)在我們打開我們的guestbook例子,添加一個(gè)新的頁面, 用不同的方式顯示數(shù)據(jù),存數(shù)據(jù)就先放著。接下來我們在這個(gè)頁面上,把數(shù)據(jù)添加到數(shù)據(jù)庫之前先添加一些驗(yàn)證,因?yàn)槲覀兊臄?shù)據(jù)庫不需要存儲(chǔ)一些沒有用的(invalid)數(shù)據(jù)。在本節(jié)結(jié)束之后,你應(yīng)該在知道怎樣構(gòu)建一個(gè)view model了,怎樣完成基本的input驗(yàn)證,應(yīng)該有個(gè)大致的了解了

4.2.1 手動(dòng)地去匹配view  models(view models 指view使用到的model)

         在第三章(上一波博客),我們已經(jīng)有了強(qiáng)類型視圖的印象,還有一個(gè)view model--為了能夠把數(shù)據(jù)更好地顯示在屏幕上,創(chuàng)建的而一個(gè)單獨(dú)的model對象。

        到目前為止,我們做的例子中,我們使用到了同樣的類(GuestbookEntry)作為我們的domain model和view model--它展現(xiàn)了我們數(shù)據(jù)庫中的信息,也是在user interface上要表示的字段,信息

        在非常小的項(xiàng)目中使用,比如我們的guestbook,這還可以,但是隨著程序越來越復(fù)雜,用戶界面也復(fù)雜了,model中的數(shù)據(jù)已經(jīng)不能直接匹配了,就有必要要分成兩部分(題外話:我現(xiàn)在自己公司做的項(xiàng)目就是兩部分,一個(gè)EF生成的實(shí)體,一個(gè)擴(kuò)充的實(shí)體,都已Dto結(jié)尾,只是為了更方便的顯示數(shù)據(jù)),正是由于這個(gè),我們應(yīng)該有能力可以把domain model轉(zhuǎn)換成view model,讓數(shù)據(jù)更容易在user interface顯示

        我們就寫個(gè)例子吧,向我們的Guestbook項(xiàng)目上添加一個(gè)頁面:做一個(gè)總結(jié)頁面,知道每一個(gè)人發(fā)了多少個(gè)comment,首先我們創(chuàng)建一個(gè)view model(view model專門讓頁面好展現(xiàn)數(shù)據(jù),就看你怎么設(shè)計(jì)了)

        在Models文件下建立CommentSummary.cs

       

        我們現(xiàn)在需要?jiǎng)?chuàng)建一個(gè)controller action----返回一個(gè)CommentSummary集合,我們在GuestbookController中寫action

   1:    public ActionResult CommentSummary()
   2:          {
   3:              var entries = from entry in _db.Entries
   4:                            group entry by entry.name into groupByName
   5:                            orderby groupByName.Count() descending
   6:                            select new CommentSummary
   7:                            {
   8:                                NumberOfComments = groupByName.Count(),
   9:                                UserName = groupByName.Key
  10:                            };
  11:              return View(entries.ToList());
  12:   
  13:          }

我們使用linq查詢,不懂的可以看一下我的 小孩系列LINQ教程

這個(gè)映射(mapping)邏輯相當(dāng)簡單,把這些代碼寫在控制器你還是可以講得通的,但是如果這個(gè)mapping變得更加復(fù)雜(舉個(gè)例子,如果它取了很多來自不同數(shù)據(jù)源的信息,只是為了構(gòu)造view model),我們應(yīng)該把這些邏輯從controller action中移出,分離(就像以前的三層框架),讓我們的controller更加簡單,輕量級的

在view中,我們循環(huán)輸出這個(gè)列表,用table展現(xiàn)

右鍵該action名稱,保持默認(rèn),添加一個(gè)視圖

   1:   @model IEnumerable<GuestInfo.Models.CommentSummary> 
   2:   
   3:            <table> 
   4:                 <tr> 
   5:                      <th>Number of comments</th> 
   6:                      <th>User name</th> 
   7:                 </tr> 
   8:                 @foreach(var summaryRow in Model) { 
   9:                      <tr> 
  10:                           <td>@summaryRow.NumberOfComments</td> 
  11:                           <td>@summaryRow.UserName</td> 
  12:                      </tr> 
  13:                 } 
  14:            </table> 

   自動(dòng)匹配View models

    除了手動(dòng)地把domain object和view models進(jìn)行映射(mapping)以外,你也可以使用工具,比如開源的AutoMapper,像這個(gè)目的,更少的代碼就可以完成了,我們將在第11章中MVC項(xiàng)目中教你怎么使用AutoMapper

在這一節(jié),我們已經(jīng)很簡短地看了一點(diǎn)view model,但是在下一章我們將要更仔細(xì)地看,我們當(dāng)然也會(huì)研究view models和input models的不同點(diǎn)

       除了mapping操作,controller中還有個(gè)經(jīng)常用到的任務(wù),驗(yàn)證用戶輸入

 

4.2.2 驗(yàn)證用戶輸入(input validation)

        回到第二章,我們在GuestbookController.cs中的Create action接受了用戶的input(輸入)。

       

         這個(gè)action,接受了Create.cshtml  post過來的input的數(shù)據(jù)(一個(gè)GuestbookEntry對象(已經(jīng)被MVC模型綁定(model-binding)機(jī)制處理了)的表單),設(shè)置一下日期,然后我們Insert一條數(shù)據(jù)到數(shù)據(jù)庫,雖然已經(jīng)完成了,但還沒有真的完成--我們還沒有任何的驗(yàn)證。此時(shí),用戶是可以不輸入他們的name或者comment就可以提交(submit)數(shù)據(jù)的。現(xiàn)在讓我們添加一些驗(yàn)證。

         首先讓我們在GuestbookEntry類中用required特性 注解一下 Name和Message,使用using System.ComponentModel.DataAnnotations;導(dǎo)入命名空間

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.ComponentModel.DataAnnotations;
   4:  using System.Linq;
   5:  using System.Web;
   6:   
   7:  namespace GuestInfo.Models
   8:  {
   9:      public class GuestbookEntry
  10:      {
  11:          public int Id { get; set; }
  12:          [Required]
  13:          public string name { get; set; }
  14:           [Required]
  15:          public string Message { get; set; }
  16:          public DateTime DateAdded { get; set; }
  17:          
  18:      }
  19:  }

使用注解(Annotate),也是一種驗(yàn)證對象屬性的方法,Required表示這個(gè)filed不能為空,還有一些其他注解,比如StringLengthAttribute:驗(yàn)證字符串長度的。(我們將會(huì)在第6章更深入學(xué)習(xí)一下)

一旦注解了,當(dāng)Create action被調(diào)用的時(shí)候,MVC將會(huì)自動(dòng)地驗(yàn)證這些屬性,看是否驗(yàn)證通過,我們可以使用ModelState.IsValid屬性,然后就可以做決定了,怎么處理。修改一下Create

   1:   
   2:          [HttpPost]//限制只能是HTTP Post 能夠訪問這個(gè)方法
   3:          public ActionResult Create(GuestbookEntry entry)
   4:          {
   5:              if (ModelState.IsValid)
   6:              {
   7:                  entry.DateAdded = DateTime.Now;
   8:                  _db.Entries.Add(entry);
   9:                  _db.SaveChanges();
  10:                  return RedirectToAction("Index");
  11:              }
  12:              return View(entry);
  13:          }

   注意

     調(diào)用ModelState.IsValid實(shí)際上不會(huì)進(jìn)行表單驗(yàn)證,它只是檢查驗(yàn)證是失敗還是成功。驗(yàn)證發(fā)生在Controller action被調(diào)用之前

我們可以調(diào)用Html.ValidationSummary方法,當(dāng)驗(yàn)證失敗的時(shí)候,顯示錯(cuò)誤信息

運(yùn)行頁面,還沒輸入任何內(nèi)容點(diǎn)擊提交:

使用這些助手,MVC將會(huì)自動(dòng)檢測驗(yàn)證錯(cuò)誤的信息,還應(yīng)用了一個(gè)css樣式,因?yàn)槲覀兊某绦蚴腔谀J(rèn)的MVC項(xiàng)目模版上寫的,這個(gè)無效的信息,將會(huì)顯示淺紅色背景色

這個(gè)錯(cuò)誤的信息內(nèi)容你可以這樣改,在Required加參數(shù)

同樣地如果你不想 硬編碼消息,想要依賴于通過資源文件,支持本地化,你可以指定ResourceName和Resource type

例如:

   1:   [Required(ErrorMessageResourceType=typeOf(MyResources),ErrorMessageResourceName="RequiredMessageError")]
   2:          public string Message { get; set; }

 

 

4.3 單元測試介紹

         在這一節(jié),我們將要簡短地看一下測試controllers。有很多測試的方法,這里我們主要講:unit testing

         單元測試很小,腳本測試,使用的語言和你項(xiàng)目中的語言一樣。為了驗(yàn)證某個(gè)方法對不對,能不能達(dá)到想要的效果就去單獨(dú)隔離式地測試某個(gè)組件的方法或函數(shù)。隨著程序的變大,單元測試也會(huì)變多,看到一個(gè)應(yīng)用程序有成百甚至上千個(gè)單元測試都是很正常的,它們可以在任何時(shí)間執(zhí)行,去驗(yàn)證bug,讓bug不會(huì)再發(fā)生

          為了讓單元測試運(yùn)行地更快,我們不能調(diào)用進(jìn)程外面的東西,這點(diǎn)很重要。當(dāng)測試一個(gè)controller中的代碼,任何獨(dú)立的部分都是模擬的,所以主要的產(chǎn)品代碼還是controller它本身。針對這個(gè)有一種可能,控制器被設(shè)計(jì)成獨(dú)立的部分,很容易就被調(diào)用(比如說database或者web服務(wù))

          為了更高效地測試我們的GuestbookController,為了允許測試,我們要做一些修改,但是在我們做這些事情之前,讓我們看一下ASP.NET MVC中默認(rèn)的單元測試模版

   4.3.1 使用默認(rèn)的單元測試模版

        默認(rèn)地,當(dāng)你創(chuàng)建一個(gè)新的ASP.NET MVC項(xiàng)目的時(shí)候,VS會(huì)提供一個(gè)創(chuàng)建單元測試項(xiàng)目的選項(xiàng)(在第二章我們已經(jīng)見過了)

如果你選擇一個(gè)創(chuàng)建單元測試項(xiàng)目,vs將會(huì)生成一個(gè)using Visual Studio Unit Testing FrameWork。這個(gè)單元測試項(xiàng)目包括了一些示例的測試,比如在HomeControllerTest類可以看見

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:  using System.Web.Mvc;
   6:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   7:  using GuestBook;
   8:  using GuestBook.Controllers;
   9:   
  10:  namespace Guestbook.Tests.Controllers
  11:  {
  12:      [TestClass]
  13:      public class HomeControllerTest
  14:      {
  15:          [TestMethod]
  16:          public void Index()
  17:          {
  18:              // 排列
  19:              HomeController controller = new HomeController();
  20:   
  21:              // 操作
  22:              ViewResult result = controller.Index() as ViewResult;
  23:   
  24:              // 斷言
  25:              Assert.AreEqual("修改此模板以快速啟動(dòng)你的 ASP.NET MVC 應(yīng)用程序。", result.ViewBag.Message);
  26:          }
  27:   
  28:          [TestMethod]
  29:          public void About()
  30:          {
  31:              // 排列
  32:              HomeController controller = new HomeController();
  33:   
  34:              // 操作
  35:              ViewResult result = controller.About() as ViewResult;
  36:   
  37:              // 斷言
  38:              Assert.IsNotNull(result);
  39:          }
  40:   
  41:          [TestMethod]
  42:          public void Contact()
  43:          {
  44:              // 排列
  45:              HomeController controller = new HomeController();
  46:   
  47:              // 操作
  48:              ViewResult result = controller.Contact() as ViewResult;
  49:   
  50:              // 斷言
  51:              Assert.IsNotNull(result);
  52:          }
  53:      }
  54:  }

         

看代碼,跟以前其他 客戶端形式的單元測試還是很像的,很好就學(xué)會(huì)了,這里我就不多說了

 

   4.3.2 測試GuestbookController

          有一些事情,GuestbookController的實(shí)現(xiàn)直接初始化,使用了GuestContext對象,這個(gè)對象訪問數(shù)據(jù)庫。這就是說沒有數(shù)據(jù)庫的前提下,要想正確的獲得數(shù)據(jù),這個(gè)測試不可能進(jìn)行,這是一個(gè)集成測試(Integration Test),而不是單元測試了

          盡管集成測試很重要,它能確保一個(gè)應(yīng)用程序的組件是否正確的在配合,也就是說如果我們對在controller中測試這個(gè)邏輯感興趣,我們不得不先測試數(shù)據(jù)庫連接。在小數(shù)量的測試,這個(gè)可能,但是如果一個(gè)項(xiàng)目有成百上千個(gè)測試,執(zhí)行時(shí)間明顯下降,因?yàn)槊恳粋€(gè)都要連接數(shù)據(jù)庫。這個(gè)問題的解決方案是減弱控制器中的GuestbookContext對象

          不直接使用GuestbookContext,我們介紹一個(gè)repository,它提供了一個(gè)入口,在處理數(shù)據(jù)訪問操作,操作GuestbookEntry對象,我們?yōu)槲覀兊膔epository寫個(gè)接口

這個(gè)接口定義了4個(gè)方法,匹配4個(gè)查詢,對應(yīng)GuestbookController

我們定義了包含查詢邏輯的概念上的實(shí)現(xiàn)

   1:  using GuestInfo.Models;
   2:  using System;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using System.Text;
   6:  using System.Threading.Tasks;
   7:   
   8:  namespace Guestbook.Contract
   9:  {
  10:      public class GuestbookRepository : IGuestbookRepository
  11:      {
  12:          private GuestbookContext _db = new GuestbookContext();
  13:   
  14:          public IList<GuestbookEntry> GetMostRecentEntries()
  15:          {
  16:              return (from entry in _db.Entries
  17:                      orderby entry.DateAdded descending
  18:                      select entry).Take(20).ToList();
  19:          }
  20:   
  21:          public void AddEntry(GuestbookEntry entry)
  22:          {
  23:              entry.DateAdded = DateTime.Now;
  24:              _db.Entries.Add(entry);
  25:             _db.SaveChanges();
  26:   
  27:          }
  28:   
  29:          public GuestbookEntry FindById(int id)
  30:          {
  31:              var entry = _db.Entries.Find(id);
  32:              return entry;
  33:          }
  34:   
  35:          public IList<CommentSummary> GetCommentSummary()
  36:          {
  37:              var entries = from entry in _db.Entries
  38:                            group entry by entry.Name into groupedByName
  39:                            orderby groupedByName.Count() descending
  40:                            select new CommentSummary
  41:                            {
  42:                                NumberOfComments = groupedByName.Count(),
  43:                                UserName = groupedByName.Key
  44:                            };
  45:              return entries.ToList();
  46:          }
  47:      }
  48:  }

接下來我們在GuestbookController中添加一些代碼,把接口引進(jìn)來

修改代碼:

using Guestbook.Contract;
using GuestInfo.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace GuestInfo.Controllers
{
    public class GuestbookController : Controller
    {
        private GuestbookContext _db = new GuestbookContext();
        IGuestbookRepository _respository;
        public GuestbookController() {
            _respository = new GuestbookRepository();
        }
        public GuestbookController(IGuestbookRepository gu)
        {
            _respository = gu;
        }
        //
        // GET: /Guestbook/
 
        public ActionResult Index()
        {
            ////Shift+Tab減少縮進(jìn),Tab增加縮進(jìn)
            //var mostRecentEntries = (from o in _db.Entries orderby o.DateAdded descending select o).Take(20);
            //// ViewBag.Entries = mostRecentEntries.ToList();
            //var model = mostRecentEntries.ToList();
 
            var mostRecent = _respository.GetMostRecentEntries();
            return View(mostRecent);
        }
 
        public ActionResult Create()
        {
            return View();
        }
 
        [HttpPost]//限制只能是HTTP Post 能夠訪問這個(gè)方法
        public ActionResult Create(GuestbookEntry entry)
        {
            if (ModelState.IsValid)
            {
                //entry.DateAdded = DateTime.Now;
                //_db.Entries.Add(entry);
                //_db.SaveChanges();
                _respository.AddEntry(entry);
                return RedirectToAction("Index");
            }
            return View(entry);
        }
 
        public ActionResult Show(int id)
        {
            //var entry = _db.Entries.Find(id);  //找到該Id的Entries
 
 
 
 
            var entry = _respository.FindById(id);
            bool hasPermission = User.Identity.Name == entry.name; //如果登陸人的姓名等于entry錄入人的姓名,就顯示Edit按鈕
            ViewData["hasPermission"] = hasPermission;
            ViewBag.hasPermission = hasPermission;
            return View(entry);
        }
 
        public ActionResult CommentSummary()
        {
            //var entries = from entry in _db.Entries
            //              group entry by entry.name into groupByName
            //              orderby groupByName.Count() descending
            //              select new CommentSummary
            //              {
            //                  NumberOfComments = groupByName.Count(),
            //                  UserName = groupByName.Key
            //              };
 
 
            var entries = _respository.GetCommentSummary();
            return View(entries.ToList());
 
        }
 
 
    }
}

雖然我們已經(jīng)把邏輯查詢部分移出了controller,但是仍然需要查詢和測試。它不再是單元測試的一部分,而是一個(gè)集成測試,鍛煉概念上的respository實(shí)例訪問數(shù)據(jù)庫。

    依賴注入(Dependence Injection)

      調(diào)把依賴性傳進(jìn)對象的構(gòu)造函中的技術(shù)被稱為依賴注入,我們已經(jīng)手動(dòng)地完成了依賴注入,通過在我們的類中添加很多構(gòu)造函數(shù)。在第18章,我們將學(xué)習(xí)怎樣使用依賴注入容器去避免這個(gè)多構(gòu)造函數(shù)的需求。更多關(guān)于依賴注入的信息 http://manning.com/seeman

 

此時(shí)我們已經(jīng)有能力繞開數(shù)據(jù)庫測試我們的controller actions了,我們達(dá)到這個(gè)目的,我們要在我們的IGuestbookRepository接口的實(shí)現(xiàn)類做些假數(shù)據(jù)。我們將創(chuàng)建一個(gè)新類,實(shí)現(xiàn)這個(gè)接口,把所有的操作放進(jìn)內(nèi)存中,我們將使用mocking framework比如說moq,RHino Mocks(都可以通過Nuget安裝),他們能夠?yàn)槲覀冏詣?dòng)創(chuàng)建含有假數(shù)據(jù)的接口的實(shí)現(xiàn)類

   1:  using Guestbook.Contract;
   2:  using GuestInfo.Models;
   3:  using System;
   4:  using System.Collections.Generic;
   5:  using System.Linq;
   6:  using System.Web;
   7:   
   8:  namespace GuestInfo.Interface
   9:  {
  10:      public class FakeGuestbookRepository : IGuestbookRepository
  11:      {
  12:          private List<GuestbookEntry> _entries
  13:              = new List<GuestbookEntry>();
  14:   
  15:          public IList<GuestbookEntry> GetMostRecentEntries()
  16:          {
  17:              return new List<GuestbookEntry> 
  18:             { 
  19:                  new GuestbookEntry 
  20:                  { 
  21:                       DateAdded = new DateTime(2011, 6, 1), 
  22:                       Id = 1, 
  23:                       Message = "Test message", 
  24:                       name = "Jeremy" 
  25:                  } 
  26:             };
  27:          }
  28:   
  29:          public void AddEntry(GuestbookEntry entry)
  30:          {
  31:              _entries.Add(entry);
  32:          }
  33:          public GuestbookEntry FindById(int id)
  34:          {
  35:              return _entries.SingleOrDefault(x => x.Id == id);
  36:          }
  37:   
  38:          public IList<CommentSummary> GetCommentSummary()
  39:          {
  40:              return new List<CommentSummary> 
  41:                     { 
  42:                          new CommentSummary 
  43:                          { 
  44:                               UserName = "Jeremy", NumberOfComments = 1 
  45:                          } 
  46:                     };
  47:          }
  48:      }
  49:  }

這個(gè)假實(shí)現(xiàn)暴露了相同的方法作為一個(gè)真實(shí)的版本,

除了那些簡單的內(nèi)存的集合,GetCommentSummary和GetMostRecentEntries方法是不能夠響應(yīng)獲得的,其他方法都可以獲得進(jìn)行測試。新建一個(gè)單元測試的項(xiàng)目

這是兩個(gè)測試用例

   1:     [TestMethod] 
   2:           public void Index_RendersView() 
   3:            { 
   4:                var controller = new GuestbookController( 
   5:                    new FakeGuestbookRepository()); 
   6:                var result = controller.Index() as ViewResult; 
   7:                Assert.IsNotNull(result); 
   8:            } 
   9:   
  10:            [TestMethod]                                                           
  11:           public void Index_gets_most_recent_entries()                                                                                                                                   
  12:            { 
  13:                var controller = new GuestbookController( 
  14:                    new FakeGuestbookRepository()); 
  15:                var result = (ViewResult)controller.Index(); 
  16:                var guestbookEntries = (IList<GuestbookEntry>) result.Model; 
  17:                Assert.AreEqual(1, guestbookEntries.Count); 
  18:   
  19:            } 
可以試著做做測試,代碼對不對,我新建的單元測試System.Web.Mvc這個(gè)引用找不到,測試沒做,但是如果你在新建項(xiàng)目后,與項(xiàng)目一起創(chuàng)建了測試模版,完全可以很順利的測試,我太懶了,但是思想已經(jīng)講到了,估計(jì)你們也會(huì)了。
凌晨兩點(diǎn)半了,我暈~
為了讓我有動(dòng)力能夠完成一下篇博客的編寫,還希望你們多多支持!
 關(guān)于ASP.NET MVC4 IN ACTION系列目錄地址已經(jīng)生成:點(diǎn)擊查看目錄
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
[ASP.NET MVC 小牛之路]09 - Controller 和 Action (1)
完成了單元測試,你的測試之旅才剛上路
【翻譯】MVC vs. MVP vs. MVVM
用Model
Laravel的中大型項(xiàng)目構(gòu)架
ASP.NET MVC Framework與WCSF中MVP模式之小小比較
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服