共用方式為


檢查編輯方法與編輯檢視 (VB)

Rick Anderson

本教學課程將說明基本概念,簡介如何使用 Microsoft Visual Web Developer 2010 Express Service Pack 1 (Visual Studio Microsoft 的免費版本) 建置 ASP.NET MVC Web 應用程式。 開始之前,請確定您已安裝下列必要項目。 您可按以下連結安裝所有專案:Web Platform Installer。 或者可使用下列連結個別安裝必要條件:

如果您使用 Visual Studio 2010 而非 Visual Web Developer 2010,請按以下連結安裝必要條件:Visual Studio 2010 必要條件

本主題隨附內含 VB.NET 原始程式碼的 Visual Web Developer 專案。 下載 VB.NET 版本。 如果您偏好使用 C#,請改參閱本教學課程的 C# 版。

在本節中,我們將檢查針對電影控制器產生的動作方法和檢視。 接著我們將新增自訂搜尋頁面。

執行應用程式並瀏覽至 Movies 控制器,方法是將 /Movies 附加到瀏覽器網址列中的 URL。 將滑鼠指標停留在 [編輯] 連結上,查看將連至的 URL。

此螢幕快照顯示已選取其中一部電影的 [編輯] 連結的MVC移動應用程式。

Edit 連結是由 Html.ActionLink Views\Movies\Index.vbhtml 檢視中的 方法所產生:

@Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |

此螢幕快照顯示程式碼編輯器中的 Html.ActionLink。

Html 物件是使用 WebViewPage 基底類別的屬性公開的協助程式。 協助程式的 ActionLink 方法可動態產生 HTML 超連結,輕鬆連結至控制器的動作方法。 ActionLink 方法的第一個引數是需要轉譯的連結文字 (例如 <a>Edit Me</a>)。 第二個引數是要叫用的動作方法名稱。 最後一個引數是匿名物件,會產生路由資料 (在此例中,ID 為 4)。

上圖顯示的已產生連結為 http://localhost:xxxxx/Movies/Edit/4。 預設路由{controller}/{action}/{id}會採用 URL 模式。 因此,ASP.NET 會將 http://localhost:xxxxx/Movies/Edit/4 轉譯為對 Movies 控制器提出 Edit 動作方法要求,其中參數 ID 等於 4。

您也可以使用查詢字串傳遞動作方法參數。 例如,URL http://localhost:xxxxx/Movies/Edit?ID=4 也會將參數 ID 4 傳遞給 Movies 控制器的 Edit 動作方法。

EditQueryString

開啟 Movies 控制器。 以下顯示兩種 Edit 動作方法。

'
' GET: /Movies/Edit/5

Function Edit(id As Integer) As ViewResult
    Dim movie As Movie = db.Movies.Find(id)
    Return View(movie)
End Function

'
' POST: /Movies/Edit/5

<HttpPost()>
Function Edit(movie As Movie) As ActionResult
    If ModelState.IsValid Then
        db.Entry(movie).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(movie)
End Function

請注意,第二個 Edit 動作方法的前面是 HttpPost 屬性。 此屬性指定 Edit 方法的多載只能由 POST 要求叫用。 您可以將 HttpGet 屬性套用至第一個編輯方法,但因為這是預設值,因此不一定要執行此動作。 (我們會參照將 HttpGet 屬性隱含指派為 HttpGet 方法的動作方法)。

HttpGet Edit 方法會採用電影 ID 參數,運用 Entity Framework Find 方法查詢電影,並將選取的電影傳回「編輯」檢視。 當 Scaffolding 系統建立 Edit 檢視時,它會檢查 Movie 類別,並建立程式碼為類別的每個屬性轉譯 <label><input> 元素。 以下範例示範產生的編輯檢視:

@ModelType MvcMovie.Movie

@Code
    ViewData("Title") = "Edit"
End Code

<h2>Edit</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>Movie</legend>

        @Html.HiddenFor(Function(model) model.ID)

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.Title)
            @Html.ValidationMessageFor(Function(model) model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.ReleaseDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.ReleaseDate)
            @Html.ValidationMessageFor(Function(model) model.ReleaseDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.Genre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.Genre)
            @Html.ValidationMessageFor(Function(model) model.Genre)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.Price)
            @Html.ValidationMessageFor(Function(model) model.Price)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
End Using

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

請注意檢視範本的檔案頂端有 @ModelType MvcMovie.Models.Movie 陳述式,用來指定預期的檢視範本模型為 Movie 類型。

已建立結構的程式碼會使用數個協助程式方法來簡化 HTML 標記。 Html.LabelFor 協助程式會顯示欄位的名稱 (「Title」、「ReleaseDate」、「Genre」 或 「Price」)。 Html.EditorFor 協助程式會顯示 HTML <input> 元素。 Html.ValidationMessageFor 協助程式則會顯示與該屬性相關聯的任何驗證訊息。

執行應用程式,並前往 /Movies URL。 按一下 Edit 連結。 在瀏覽器中,檢視頁面的原始檔。 頁面顯示的 HTML 外觀類似以下範例。 (為了清楚起見,已排除功能表標記)。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Edit</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
    <script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>MVC Movie App</h1>
            </div>
           ...
        </header>
        <section id="main">

<h2>Edit</h2>

<script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>

<form action="/Movies/Edit/4" method="post">    <fieldset>
        <legend>Movie</legend>

        <input data-val="true" data-val-number="The field ID must be a number." 
    data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

        <div class="editor-label">
            <label for="Title">Title</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" />
            <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="ReleaseDate">ReleaseDate</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-required="The ReleaseDate field is required." 
    id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" />
            <span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Genre">Genre</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" />
            <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Price">Price</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." 
    data-val-required="The Price field is required." id="Price" name="Price" type="text" value="9.99" />
            <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>
<div>
    <a href="/Movies">Back to List</a>
</div>

        </section>
        <footer>
        </footer>
    </div>
</body>
</html>

<input> 元素位於 HTML <form> 元素中,後者的 action 屬性設為張貼到 /Movies/Edit URL。 按一下 [編輯] 按鈕,表單資料就會張貼至伺服器。

處理 POST 要求

下列清單顯示 HttpPost 版本的 Edit 動作方法。

'
' POST: /Movies/Edit/5

<HttpPost()>
Function Edit(movie As Movie) As ActionResult
    If ModelState.IsValid Then
        db.Entry(movie).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(movie)
End Function

ASP.NET 架構模型繫結器會採用已張貼的表單值,並建立 Movie 物件 (以 movie 參數形式傳遞)。 ModelState.IsValid 會檢查程式碼,確認表單中提交的資料可用於修改 Movie 物件。 如果資料有效,程式碼會將電影資料儲存至 MovieDBContext 執行個體的 Movies 集合。 接著,程式碼會呼叫 MovieDBContextSaveChanges 方法,將新的電影資料儲存至資料庫,以保存資料庫變更。 儲存資料之後,程式碼會將使用者重新導向至 MoviesController 類別的 Index 動作方法,因此電影清單會顯示已更新的電影。

如果張貼的值無效,表單會重新顯示值。 Html.ValidationMessageFor Edit.vbhtml 檢視範本中的協助程式會負責顯示適當的錯誤訊息。

abcNotValid

地區設定的注意事項若您平常都使用英文以外的地區設定,請參閱「支援使用非英文地區設定的 ASP.NET MVC 3 驗證」。

強化編輯方法

建構系統產生的 HttpGet Edit 方法不會檢查其中傳遞的識別碼是否有效。 如果使用者從 URL (http://localhost:xxxxx/Movies/Edit) 移除識別碼區段,就會顯示下列錯誤:

Null_ID

使用者也可以傳遞不存在於資料庫的識別碼,例如 http://localhost:xxxxx/Movies/Edit/1234。 您可以對 HttpGet Edit 動作方法進行兩項變更,以因應這項限制。 首先,若系統未明確傳遞識別碼,請將 ID 參數變更為預設值等於零。 將電影物件傳回至檢視範本之前,您也可以檢查 Find 方法是否已實際找到電影。 更新的 Edit 方法顯示如下。

Public Function Edit(Optional ByVal id As Integer = 0) As ActionResult
    Dim movie As Movie = db.Movies.Find(id)
    If movie Is Nothing Then
        Return HttpNotFound()
    End If
    Return View(movie)
End Function

若找不到電影,則呼叫 HttpNotFound 方法。

所有 HttpGet 方法都遵循類似的模式。 它們會取得電影物件 (在 Index 的案例為物件清單),並將模型傳遞至檢視。 Create 方法會將空白電影物件傳遞至「建立」檢視。 建立、編輯、刪除或以其他方式修改資料的所有方法都會在方法的 HttpPost 多載中執行這個動作。 修改 HTTP GET 方法中的數據是安全性風險。 修改 GET 方法的資料也不符合 HTTP 最佳做法及架構 REST 模式,此模式會指定 GET 要求不應變更應用程式的狀態。 換句話說,執行 GET 作業應該要安全無虞,沒有其他風險考量。

新增搜尋方法和搜尋檢視

在本節中,您將新增 SearchIndex 動作方法,其可依「類型」或「名稱」搜尋電影。 使用 /Movies/SearchIndex URL 即可取得。 要求會顯示 HTML 表單,其中包含使用者可填入的輸入元素,以便搜尋電影。 使用者提交表單時,動作方法會取得使用者張貼的搜尋值,並將值用於搜尋資料庫。

SearchIndx_SM

顯示 SearchIndex 表單

首先,將 SearchIndex 動作方法新增至現有的 MoviesController 類別。 方法會傳回包含 HTML 表單的檢視。 程式碼如下:

Public Function SearchIndex(ByVal searchString As String) As ActionResult
    Dim movies = From m In db.Movies
                 Select m 

    If Not String.IsNullOrEmpty(searchString) Then 
        movies = movies.Where(Function(s) s.Title.Contains(searchString)) 
    End If
    Return View(movies) 
End Function

SearchIndex 方法的第一行會建立以下 LINQ 查詢來選取電影:

Dim movies = From m In db.Movies    Select m

目前我們已定義查詢,但尚未對資料存放區執行查詢。

如果 searchString 參數包含字串,則系統會使用下列程式碼修改電影查詢,以篩選搜尋字串的值:

如果不是 String.IsNullOrEmpty(searchString) 則
movies = movies。Where(Function(s) s.Title.Contains(searchString))
結束 If

在您定義 LINQ 查詢,或藉由呼叫 WhereOrderBy 這類方法修改 LINQ 查詢時,它並不會執行。 反之,查詢的執行作業將會延遲,即評估運算式會延遲,直到系統逐一查看實現值或呼叫 ToList 方法。 在 SearchIndex 範例中,查詢將於 SearchIndex 檢視中執行。 如需延後查詢執行的詳細資訊,請參閱查詢執行

接下來,您可以實作 SearchIndex 檢視,向使用者顯示表單。 在 SearchIndex 方法內按滑鼠右鍵,然後按一下 [新增檢視]。 在 [新增檢視] 對話方塊中,指定您要將 Movie 物件傳遞至檢視範本做為模型類別。 在「Scaffold 範本」清單中選擇 [清單],然後按一下 [新增]

AddSearchView

當您按兩下 [ 新增 ] 按鈕時, 會建立 Views\Movies\SearchIndex.vbhtml 檢視範本。 由於您在 Scaffold 範本清單中選取 [清單],因此 Visual Web Developer 會自動在檢視中產生 (建構) 預設內容。 Scaffolding 建立了 HTML 表單。 其中檢查了 Movie 類別,並建立程式碼呈現類別中每個屬性的 <label> 元素。 下列清單顯示產生的建立檢視:

@ModelType IEnumerable(Of MvcMovie.Movie)

@Code
    ViewData("Title") = "SearchIndex"
End Code

<h2>SearchIndex</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th>
            Title
        </th>
        <th>
            ReleaseDate
        </th>
        <th>
            Genre
        </th>
        <th>
            Price
        </th>
        <th></th>
    </tr>

@For Each item In Model
    Dim currentItem = item
    @<tr>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Title)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.ReleaseDate)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Genre)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Price)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |
            @Html.ActionLink("Details", "Details", New With {.id = currentItem.ID}) |
            @Html.ActionLink("Delete", "Delete", New With {.id = currentItem.ID})
        </td>
    </tr>
Next

</table>

執行應用程式,並瀏覽至 /Movies/SearchIndex。 將查詢字串 (例如 ?searchString=ghost) 附加至 URL。 隨即顯示篩選過的電影。

SearchQryStr

如果您將 SearchIndex 方法的簽章變更為包含名為 id 的參數,則 id 參數會針對 Global.asax 檔案中設定的預設路由,比對 {id} 預留位置。

{controller}/{action}/{id}

已修改的 SearchIndex 方法如下所示:

Public Function SearchIndex(ByVal id As String) As ActionResult
Dim searchString As String = id
Dim movies = From m In db.Movies
             Select m

If Not String.IsNullOrEmpty(searchString) Then
    movies = movies.Where(Function(s) s.Title.Contains(searchString))
End If

Return View(movies)
End Function

您現在可以將搜尋標題作為路由資料 (URL 區段) 傳遞,而不是作為查詢字串值。

SearchRouteData

但是,您不能期望使用者在每次想要搜尋電影時修改 URL。 現在您可新增 UI 來協助篩選電影。 如果您已變更 SearchIndex 方法的簽章,藉此測試如何傳遞路由繫結的 ID 參數,請改回原狀,使 SearchIndex 方法採用名為 searchString 的字串參數:

開啟 Views\Movies\SearchIndex.vbhtml 檔案,然後在 之後@Html.ActionLink("Create New", "Create")新增下列內容:

@Code
    ViewData("Title") = "SearchIndex"
    Using (Html.BeginForm())
         @<p> Title: @Html.TextBox("SearchString") 
         <input type="submit" value="Filter" /></p>
        End Using
End Code

Html.BeginForm 協助程式會建立開頭的 <form> 標記。 使用者按一下 [篩選] 按鈕提交表單後,Html.BeginForm 協助程式會將表單張貼至表單本身。

執行應用程式並嘗試搜尋電影。

SearchIndxIE9_title

SearchIndex 方法沒有 HttpPost 多載。 您不需要多載,因為此方法不會變更應用程式的狀態,只會篩選資料。 如果您新增了下列HttpPostSearchIndex方法,動作叫用程式會符合 SearchIndex HttpPost 方法,而且 HttpPost SearchIndex 方法會執行,如下圖所示。

<HttpPost()>
 Public Function SearchIndex(ByVal fc As FormCollection, ByVal searchString As String) As String
     Return "<h3> From [HttpPost]SearchIndex: " & searchString & "</h3>"
 End Function

SearchPostGhost

新增以類型為依據的搜尋

如果您已新增 SearchIndex 方法的 HttpPost 版本,請先將它刪除。

接下來,您會新增一項功能,讓使用者依據類型搜尋電影。 以下列程式碼取代 SearchIndex 方法:

Public Function SearchIndex(ByVal movieGenre As String, ByVal searchString As String) As ActionResult
    Dim GenreLst = New List(Of String)()

    Dim GenreQry = From d In db.Movies
                   Order By d.Genre
                   Select d.Genre
    GenreLst.AddRange(GenreQry.Distinct())
    ViewBag.movieGenre = New SelectList(GenreLst)

    Dim movies = From m In db.Movies
                 Select m

    If Not String.IsNullOrEmpty(searchString) Then
        movies = movies.Where(Function(s) s.Title.Contains(searchString))
    End If

    If String.IsNullOrEmpty(movieGenre) Then
        Return View(movies)
    Else
        Return View(movies.Where(Function(x) x.Genre = movieGenre))
    End If

End Function

這個版本的 SearchIndex 方法會採用額外參數,也就是 movieGenre。 前幾行程式碼會建立 List 物件,藉此保存資料庫中的電影類型。

下列程式碼是一種 LINQ 查詢,其會從資料庫中擷取所有的內容類型。

Dim GenreQry = From d In db.Movies
                   Order By d.Genre
                   Select d.Genre

程式碼會使用一般的 List 集合的 AddRange 方法,將所有不同類型新增至清單。 (如果沒有 Distinct 修飾詞,系統會新增重複的類型,例如在我們的範例中,喜劇就會新增兩次)。 程式碼接著會將類型清單儲存在 ViewBag 物件中。

下列程式碼示範如何檢查 movieGenre 參數。 如果參數不是空白,程式碼會進一步限制電影查詢,將選取的電影限為指定類型。

If String.IsNullOrEmpty(movieGenre) Then
        Return View(movies)
    Else
        Return View(movies.Where(Function(x) x.Genre = movieGenre))
    End If

將標記新增到 SearchIndex 檢視,支援依類型搜尋

Html.DropDownList將協助程式新增至 Views\Movies\SearchIndex.vbhtml 檔案,就在協助程式之前TextBox。 完成的標記如下所示:

<p>
    @Html.ActionLink("Create New", "Create")
    @Code
    ViewData("Title") = "SearchIndex"
    Using (Html.BeginForm())
         @<p> Genre: @Html.DropDownList("movieGenre", "All")
         Title: @Html.TextBox("SearchString") 
         <input type="submit" value="Filter" /></p>
        End Using
End Code
</p>

執行應用程式,並瀏覽至 /Movies/SearchIndex。 嘗試依類型、電影名稱搜尋,以及同時用這兩個條件搜尋。

在本節中,您檢查了由架構產生的 CRUD 動作方法和檢視。 您建立了搜尋動作方法和檢視,讓使用者依電影標題和類型進行搜尋。 下一節,您將瞭解如何將屬性新增至 Movie 模型,以及如何新增初始設定式,自動建立測試資料庫。