2015年4月8日 星期三

初學 ASP.NET MVC 藉由 Scaffold 認識 C.R.U.D 的操作

時常會看到初學 ASP.NET MVC 的朋友在一開始進入學習或實作時總是會不知所措,如果是有 ASP.NET Web Forms 開發經驗的人但是對於物件導向、ORM 觀念和 LINQ 操作都不熟悉,之前比較擅長的資料操作就是 DataSet, DataTable,網頁就是在各種伺服器控制項或是在 Code-Behind 組字串並拋到前端處理,這樣類型的開發者在轉換到 ASP.NET MVC 時都會想要將以往 ASP.NET Web Forms 的開發經驗給轉移過去,但是兩種開發模式相差蠻大的,再加上觀念也有差異,所以在開發上就很容易陷入困境。

另外一種就是完完全全的初學者,可能之前沒有接觸過任何的網頁程式設計,或是有開發過其他技術的網頁程式,但是對於 .NET 這一塊領域並不熟悉,所以在很多觀念與操作上都相當生疏。

其實我一再地說,其實網路上有很多學習資源可以加以利用,只要肯花時間去從頭開始學起,基本上很多問題都會迎刃而解,但實際情況往往都是很多開發者都想直接跳過入門學習的階段,而想要直接實戰下去,這樣的學習往往會弄得自己千瘡百孔,搞到最後不是興趣缺缺、對 ASP.NET MVC 多有微詞之外,最怕的就是為了產出結果而搞出一些怪招怪式,這在往後的開發上是一點幫助也沒有。

這邊就直接以最基本的使用基架建立 C.R.U.D(Create, Read, Update, Delete) 的方式來認識怎麼開發 ASP.NET MVC 的第一課。

 


使用開發工具:Visual Studio 2013

使用版本:ASP.NET MVC 5, LocalDB, Entity Framework 6

範例資料庫:Northwind ( 也可以改用你自己已經先建立好的資料庫 )
https://northwinddatabase.codeplex.com/

這一篇的內容只是藉由 Scaffold 快速產生 Controller 內容與檢視頁面,以導覽的方式去瞭解 Controller 的程式和檢視頁面的內容,並不是相當詳盡的細節操作說明,我的用意在於先從這種快速建立的方式去認識 ASP.NET MVC 的各個組成部分,先有個基礎的認識與瞭解,這樣的方式會比從無到有的去慢慢摸索要來得清楚一些。

 

Step.1 - 建立一個新的 ASP.NET MVC 5 專案

SNAGHTML1335b624

在專案建立完成之後,預設都會在 Visual Studio 中間開啟以下的內容(Project_Readme.html),

image

很多人都會忽略,但這裡有很多資訊是可以看的,例如第一個項目「開始使用 ASP.NET MVC」,連結會引導到 ASP.NET MVC 官網的教學課程「Getting Started with ASP.NET MVC 5」,花點時間,好好的跟著課程一步一步地做,基本的 ASP.NET MVC 操作就能夠上手,可別因為是英文內容就畏懼,要知道以後找問題的解決參考還是以英文的內容比較多。

image

另外也花點時間去看看官網的內容,在下圖用紅線所框起來的部分,每個項目展開的連結裡,都有相當豐富的教學內容,真的不要忽略官網呀。

image

 

Step.2 - 建立 LoacalDB 與 ADO.NET 實體資料模型

如果你是使用自己所建立的資料庫,那麼就跳過建立 LoaclDB 的這一段,而建立 ADO.NET 實體資料模型則是使用 Database First,你也可以使用 Code First,這邊不會另外說明。

SNAGHTML2cd6c2

建立 LocalDB 之後,在 LocalDB 建立 Northwind 相關 Table 與資料
https://northwinddatabase.codeplex.com/releases/view/71634

image

接著建立 ADO.NET 實體資料模型

image

SNAGHTML432d44

SNAGHTML4391ab

SNAGHTML43dae9

SNAGHTML4433c7

到了上圖的步驟就可以直接按下「完成」按鍵,這樣就完成建立 ADO.NET 實體資料模型,以下是 ADO.NET 實體資料模型使用 Database First 方式建立模型之後所新增的 Northwind.edmx 內容,在這個 EDMX 裡可以看到各個模型是對照資料庫的 Table 所建立的,

image

記得建立完 ADO.NET 實體資料模型之後必須要執行專案的重新建置,在往後的專案的各種檔案的修改或新增,操作完成之後都要記得重新建置專案。

 

Step.3 - 新增 Controller - 使用「具有檢視、使用 Entity Framework 的 MVC 5 控制器」

在這一個步驟裡要做的是建立 Controller,

image

在「新增 Scaffold」視窗裡選擇使用「具有檢視、使用 Entity Framework 的 MVC 5 控制器」,接著就是按下右下角的「新增」按鍵,

SNAGHTML5476bd

在接下來的「加入控制器」視窗裡,要選擇「模型類別」和「資料內容模型」,模型類別選擇「Customer」,而資料內容模型選擇「NorthwindEntities」,其他的部分都使用預設的選項,最後再按下「加入」按鍵,

SNAGHTML96f1f7

接著就會依照 Scaffold 建立 CustomersController.cs 以及實作的程式內容,並且建立對映 Controller 內 Action 方法的 View 檔案,

image

 

Step.4 - CustomersController 的 Index Action 方法與 Index.cshtml

首先觀察 CustomersController 的 Index 動作方法(以下將會直接使用 Action Method)的程式內容,在 CustomersController 的類別裡建立一個 NorthwindEntities 型別的欄位,並且直接建立 instance,這麼一來在其他的 Action Method 裡就可以直接使用 db 這個欄位來對資料進行存取操作,

image

在 Index 這個 action method 裡所做的就是從 db 裡取得所有的 Customer 資料,而這些資料要傳給 View 去做顯示,所以在最後回傳時是將所有的 Customer 資料直接放在 View() 裡,在 Index action method 的地方按下滑鼠右鍵,選擇「前往檢閱」,就會開啟對映 action method 的 View 檔案,而對映 Index action method 的檢視檔案就是 Index.cshtml,

image

在 Index.cshtml 的一開始就定義了這一個 View 所要使用的模型類別,一個檢視頁面只能使用一個模型類別,在 Index 檢視頁面是要顯示很多相同類別的資料,所以是使用 IEnumerable<WebApplicatipon1.Models.Customer>。

頁面顯示結果

image

而檢視頁面裡對資料操作、資料顯示會使用 Razor Syntax 做處理,而 ASP.NET MVC 也提供了許多 Html Helper 讓我們去產生一些網頁的元件、標籤,也可以結合模型資料去產生包含資料的網頁標籤內容。

例如下圖所顯示的內容,就是使用 Html Helper 的 ActionLink 方法去產生帶有資料 ID 的連結標籤,

image

image

Introduction to ASP.NET Web Programming Using the Razor Syntax (C#) | The ASP.NET Site

ScottGu's Blog - Introducing “Razor” – a new view engine for ASP.NET

C# Razor Syntax Quick Reference - You've Been Haacked

ASP.NET MVC3 Razor 初心者容易遇到的問題 | demo小鋪

有關 Razor 語法以及 Html Helper 的詳細說明,可以參閱「ASP.NET MVC 5 網站開發美學」第六章
8-6 Razor 語法
8-7 Helpers

 

Step.5 - Details

由 Index 頁面的各列資料最後面都有 Edit, Details, Delete 連結,每個連結都會帶著資料的 ID,這個 ID 作為辨識之用,就可以知道要向資料庫取得哪一筆資料,當沒有傳入 ID 時就會回傳錯誤,如果傳入的 ID 是找不到資料的也要回傳錯誤訊息,

image

找到資料之後,就將資料傳給 View,而 Details 是顯示單一筆資料,與 Index 顯示多筆資料是不同的,所以 Details 檢視頁面所使用的就不是 IEnumerable<T> 而是 Customer 類別,

image

在檢視頁面的最下面是要顯示兩個連結,一個是直接連結到編輯頁面,連結裡帶有資料 ID ,與進入 Details 頁面的處理方式一樣,需要傳入資料 ID,才能在 action method 裡去找到相對映的資料,而另一個連結則是回到 Index 檢視頁面。

 

Step.6 - Edit 編輯頁面

前一個步驟有說到,要怎麼知道編輯哪一筆資料,就必須要在進入 Edits 時帶入資料 ID,在 Controller 裡的 Edit 會有兩個 action method,一個是用來取得要編輯的資料並且將編輯資料傳到 Edit 檢視頁面,這一個 action method 是 HttpGet(預設的 action method 的 HttpMethod 都會是 HttpGet),而從編輯頁面送回編輯過的資料給後端去儲存到資料庫,這個相對映的 action method 是使用 HttpPost,儲存編輯後資料到資料庫,最後則是回到原本的編輯頁面。

image

編輯的檢視頁面,在下面的第一張圖可以看到編輯頁面會使用 Html Helper 的 BeginForm 方法,這是用來建立 Form 表單的標籤,如果在 BeginForm 方法裡沒有特別指定 Url 路徑時,預設將為使用帶有資料 ID 的 Url 路徑,

image

image

編輯的檢視頁面

image

原始碼

image

image

將編輯頁面的資料 POST 傳回後端時,後端 Edit action method 會藉由 Form 表單內的各個 Input 欄位的 Name attribute 去對映到 Customer 類別的屬性,所以這邊要記得 ASP.NET MVC 對於表單內容的 Model Binding 處理是用 Form 表單內 Input 欄位的 Name atttibute,

image

 

Step.7 - Create 資料建立

看過了 Edit 之後再看 Create 就會比較容易瞭解,同樣也是有兩個 action method,一個是要用來顯示資料建立的檢視頁面,另一個使用 HttpPost 的 action method 則是處理 POST 回來的資料建立,

image

檢視頁面

image

檢視頁面執行結果

image

 

Step.8 - Delete 刪除

使用 Scaffold 所建立刪除 action method 會有兩個不同名稱的 action method,一個是使用預設 HttpGet 的 action method,另一個使用 HttpPost 的 action method 則是不同名稱。

第一個 action method 是顯示要刪除的資料內容,讓使用者檢視這一筆要刪除的資料,並確定是否刪除,而確定執行資料刪除的 action method 名稱則是不同,一個是 Delete 另一個則是 DeleteConfirm,但是要在檢視頁面上同樣是使用 Delete 的路徑名稱,所以在 DeleteConfirm action method 上使用 ActionName 這個特性標籤,因為 Controller 裡不可以有兩個同樣名稱與同樣參數的方法存在,所以第二個執行刪除的 action method 名稱才會使用 DeleteConfirm,但是使用 ActionName 特性標籤可以讓 Route 可以知道使用 HttpPost 的刪除要對映到哪一個 action method。

image

刪除的檢視頁面

image

image

頁面顯示

image

頁面原始碼

image

 

Step.9 - Dispose

一開始進入 CustomersController 時就會建立 NorthwindEntities 的 instance,當執行完畢之後就需要執行 Dispose,讓建立的 NorthwindEntities instance 資源釋放並回收。

image

延伸閱讀:

Huan-Lin 學習筆記: Entity Framework DbContext 物件的生命週期

 

以上就是使用 Scaffold 建立「具有檢視、使用 Entity Framework 的 MVC 5 控制器」的控制器與檢視頁面的導覽說明,在實務應用上是很少會去用到「具有檢視、使用 Entity Framework 的 MVC 5 控制器」這個選項去建立 Controller 與 View,但是在初學階段則是可以先藉由這一種建立方式對於 ASP.NET MVC 的 Controller, View 有個初淺的認識。

只是這一個部分並沒有說明到很多應該注意的地方,初學者還是需要每個部分都加以瞭解,例如 Route 的設定,Model 的 Data Annotation 與資料驗證的使用,Model Binding,Html Helper 與強型別的 Html Helper,Entity Framework 的應用等等還有很多很多需要瞭解的部分。

所以說學習 ASP.NET MVC 並不是花個幾小時就可以完成的,牽涉的地方會很多,很多觀念也需要建立或是重新打掉,所以真的要花時間下功夫去仔細研究。


當你要學習一項新技術的時候,真的不要心急,不要去想如何用這項新技術去開發出專案需求,因為在怎麼想也是沒有用的,因為你連基本的語法、方法操作都還不會,去想要怎麼做都是枉然。

真的要先正視問題,你用你所遇到的問題去 Google 上面尋求解決方法,大部分的人都只會找出相近於問題的解決方法然後複製貼上,但這樣永遠不能根本解決你所遇到的問題,往後遇到更多令人匪夷所思的狀況時,難道又是繼續到 Google 找答案,或是到論壇去碰運氣,期待有好心人可以丟給你答案嗎?

以前用 ASP.NET WebForms 時,可以用什麼樣的方式去建立整個資料的操作流程,但是當你想要用 ASP.NET MVC 去建立一樣的資料操作流程時,請務必先忘了 WebForms 的那一套方式,請先好好地認識 ASP.NET MVC 以及相關的觀念與基礎,等到有了一定基礎之後再回過頭來想怎麼去建立資料的操作流程,而且是在把 WebForms 那一套做法全部拋棄的情況下去建立。

真的不要再去想「以前 WebForms 是怎麼操作的,而 MVC 應該怎麼去把哪一套給複製過來」,除非你在 WebForms 就已經使用物件導向、ORM 以及分層的方式去處理(例如以下連結裡的做法),否則別想搬過來重複使用。

相關連結:

ASP.NET MVC 的 Model 使用 ADO.NET
ASP.NET MVC 的 Model 使用 Enterprise Library 6 Data Access Application Block
ASP.NET MVC - 使用 Simple Injector 讓 Model 三選一
ASP.NET WebForm 使用分層的 Repository 類別庫專案
ASP.NET WebForm 使用 Simple Injector 選擇不同的 Repository
ASP.NET MVC 與 ASP.NET WebForm 使用 Simple Injector 切換選擇不同 Repository 原始碼下載

 

學習資源:

ASP.NET MVC 官網教學課程
http://www.asp.net/mvc/overview

ASP.NET MVC 官網 - Learn> Videos
http://www.asp.net/mvc/videos/mvc-5

MVA - Introduction to ASP.NET MVC
http://www.microsoftvirtualacademy.com/training-courses/introduction-to-asp-net-mvc

 

以上

9 則留言:

  1. 刻板印象呀,很多人都覺得官網只會放很制式的文件,也覺得官網沒什麼資料,
    通常要帶著這些人去官網繞一圈之後才會知道 ASP.NET 官網放了一堆寶。

    回覆刪除
  2. 其實不管是 WebForm 還是 MVC 或 WebAPI, SignalR 還是其他的應用,其實都是在做 C# 與 .NET Framework 的應用,不同的只在於表現與行為使用上的差異,說到底也只是一句話,那就是基礎與觀念要打穩。

    回覆刪除
  3. Kevin Tseng 老師你好
    我有一個問題,困擾很久,我用公司的既有資料庫部門代號檔單一資料表(部門代號 型態STRING 長度4為KEY,部門名稱型態STRING 長度12,建檔日期型態STRING 長度8)做MVC CRUD 範例,但既有部門代號資料有的KEY滿4碼,有的KEY3碼,例如MARK 行銷業務部,FIN 財務部,如此一來,在EDIT,DETAIL,DELETE超連結的地方,點下去,MARK行銷業務部是OK但FIN 財務部,都出現網頁無法連結,資源無法找到,後來發現是URL的地方多了(20%)符號如ID=FIN20%,手動去掉20%,則財務部也可EDIT,DETAIL,DELETE等,為什麼?
    網路上的範例都是用INT作為KEY值但我的資料表不是且有的代號輸入不滿4碼怎麼辦?
    GOOGLE很久都找不到範例,明知是URL因為STRING 空白一格的關係就是不知如何修改CONTROLLER中EDIT,DELETE,DETAIL程式,
    或者是INDEX中的 @Html.ActionLink("Edit", "Edit", new { id=item.XXXXX01 })XXXX為部門代號;我知道要DECODE,但不知如何CODING;還是要用TRIM()去除空白才進行URL參數傳遞:抑或是修改ROUTECONFIG.CS
    還是MVC 之CRUD 無法支援以STRING型態為KEY值練習。謝謝

    回覆刪除
    回覆
    1. Hello, 你好
      先直接說,通常很少會用一個讓人輸入資料的值去做為一筆資料的 Primary Key
      部門代碼是一個可以識別的值,這點沒錯,但有沒有可能會改變?或許機會不大,但是不能排除這一個可能性,
      所以大部分一筆資料的 PK 都是一個自動產生值的欄位,型別會用 int 或是 GUID,因為 PK 值是不能隨意變更,
      再來部門代碼也不會因為改變而造成其他關連資料的影響與變更。

      如果用了一個可以讓人輸入資料的值做為識別值,前端頁面的輸入欄位就是公開,
      你永遠無法知道使用者輸入了什麼,別以為使用者只能用鍵盤輸入資料,有時候有些人還是會貼上 ASCII Code 等等的值,
      前端的輸入就必須要做到驗證的處理,而資料 POST 送出到後端前的 Javascript 也還必須要再做一次資料的驗證與淨化,
      如此一來送到後端才比較不會出錯,但是到了後端還是要對前端所送來的資料再做一次驗證與處理,
      不論是前端或是後端都必須要對資料格外的小心與多一份處理,才能保證資料的安全以及不會影響到已經存在的既有資料。

      再來就是存到資料庫以及從資料庫所取得的資料也必須額外再做一次淨化,確保資料都是乾淨的,
      所以資料的處理不見得是拿到資料就存到 DB 或是從 DB 拿出來後就直接傳給前端,沒有這麼簡單與容易,
      另外資料表的欄位,欄位存的資料是 String,所以建議要使用 NVarchar 型別而不是 Varchar,因為有 Unicode 的問題,
      這也是需要特別注意的地方。

      你的狀況應該是,最一開始的資料建立時,所存進資料庫的部門代碼就包含了空白,
      而你的程式從前端到後端甚至一路到存進資料庫、從資料庫取出的過程裡都沒有做到驗證與淨化的處理,
      程式與 DB 只是很忠實的去執行你的程式以及記錄使用者所存進去的資料,
      你所提到的狀況,不論是不是用 ASP.NET MVC 開發,甚至於是使用 ASP,NET WebForm 或 ASP, PHP 等等都會遇到的,
      Primary Key 值當然也可以使用 String 型別,只是看用在什麼地方,
      而你去網路上搜尋資料都看到大部分是用 int 或 GUID 當作 PK 的型別,這是常態,因為很少人會直接用 String 當作 KEY,
      你所遇到的只是單純的資料結構、資料驗證、資料處理等的基本觀念問題。

      一筆資料要有一個單獨的欄位去做為識別值,也就是 Primary Key,
      這一個 PK 值,除非必要,否則不可以用使用者所輸入的資料來當作 PK 值。

      刪除
  4. 謝謝老師這麼快地幫我回答,特別是觀念上的釐清(一個 PK 值,除非必要,否則不可以用使用者所輸入的資料來當作 PK 值,使用 NVarchar 型別而不是 Varchar,因為有 Unicode 的問題,這也是需要特別注意的地方。)很受用,但我這個使用string為KEY單一資料表在套用MVC之CRUD是需要更改很多程式加上JAVASCRIPT+POST就可以了嗎?我應該放棄?雖然我用WebForm處理這個Table是可以。PK值是為了此範例我在備份資料庫自己加的,沒辦法公司的ERP用很久了,資料型態早就訂了,不知道MVC美學第5版這本書有沒有解答
    原有設定值只是唯一KEY




    回覆刪除
    回覆
    1. 北風資料庫的CUSTOMER的CUSTOMERid即符合為string的資料表

      刪除
    2. Northwind 是個範例資料庫,而且他在應用程式是如何管理的,這個我們就不得而知,
      早期初學程式的時候,我也是直接讓使用者自己輸入資料,然後就直接用那個欄位的資料來當作 PK 值,
      也是會直接使用 String 型別(varchar 或 nvarchar)做為 PK 欄位的型別,
      如果一定要這麼做的話,前提是使用者要知道一旦輸入了就無法對這個資料做事後的更改,
      然後前後端的資料驗證以及資料值的淨化就必須要做到徹底,這麼一來就不會需要去更動到既有資料庫的欄位型別,
      重點還是在於對輸入與輸出的資料要做驗證與淨化處理。

      刪除
  5. 作者已經移除這則留言。

    回覆刪除

提醒

千萬不要使用 Google Talk (Hangouts) 或 Facebook 及時通訊與我聯繫、提問,因為會掉訊息甚至我是過了好幾天之後才發現到你曾經傳給我訊息過,請多多使用「詢問與建議」(在左邊,就在左邊),另外比較深入的問題討論,或是有牽涉到你實作程式碼的內容,不適合在留言板裡留言討論,請務必使用「詢問與建議」功能(可以夾帶檔案),謝謝。