將資料傳遞至檢視主版頁面 (C#)
由 Microsoft 提供
本教學課程的目標是說明如何將資料從控制器傳遞至檢視主版頁面。 我們會檢查將資料傳遞至檢視主版頁面的兩種策略。 我們會先討論一個簡單的解決方案,但產生難以維護的應用程式。 接下來,我們會採用較妥善的解決方案進行檢查,這需要更多初始工作,但產生的應用程式較容易維護。
將資料傳遞至檢視主版頁面
本教學課程的目標是說明如何將資料從控制器傳遞至檢視主版頁面。 我們會檢查將資料傳遞至檢視主版頁面的兩種策略。 我們會先討論一個簡單的解決方案,但產生難以維護的應用程式。 接下來,我們會採用較妥善的解決方案進行檢查,這需要更多初始工作,但產生的應用程式較容易維護。
問題
假設您正在建置電影資料庫應用程式,且想在應用程式的每個頁面上顯示電影類別清單 (請見圖 1)。 另外您可以想像一下,電影類別清單將儲存在資料庫資料表中。 在此情況下,從資料庫擷取類別,並在檢視主版頁面中轉譯電影類別清單就很合理了。
圖 01:在檢視主版頁面中顯示電影類別 (按一下以檢視完整大小的影像)
問題來了。 要如何在主版頁面中擷取電影類別清單? 直接在主版頁面中呼叫模型類別方法,這個想法很吸引人。 也就是說,直接在主版頁面中納入程式碼以從資料庫擷取資料,這個想法很吸引人。 不過,如果略過 MVC 控制器去存取資料庫,將違反清楚區隔的原則 (這是建置 MVC 應用程式的主要優點之一)。
在 MVC 應用程式中,您希望由 MVC 控制器處理 MVC 檢視與 MVC 模型之間的所有互動。 這種分區原則會讓應用程式更容易維護、可調整且可測試。
在 MVC 應用程式中,傳遞至檢視的所有資料 (包括檢視主版頁面) 都應由控制器動作傳遞至檢視。 此外,您應利用檢視資料來傳遞資料。 在本教學課程的其餘部分,我會介紹兩種將資料傳遞至檢視主版頁面的方法。
簡單的解決方案
我們先從最簡單的解決方案開始,將檢視資料從控制器傳遞至檢視主版頁面。 最簡單的解決方案是在每個控制器動作中傳遞主版頁面的檢視資料。
請考慮清單 1 中的控制器。 這會公開兩個動作:Index()
和 Details()
。 Index()
動作方法會傳回電影資料庫資料表中的每部電影。 Details()
動作方法會傳回特定電影類別中的每部電影。
清單 1 – Controllers\HomeController.cs
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
[HandleError]
public class HomeController : Controller
{
private MovieDataContext _dataContext = new MovieDataContext();
/// <summary>
/// Show list of all movies
/// </summary>
public ActionResult Index()
{
ViewData["categories"] = from c in _dataContext.MovieCategories
select c;
ViewData["movies"] = from m in _dataContext.Movies
select m;
return View();
}
/// <summary>
/// Show list of movies in a category
/// </summary>
public ActionResult Details(int id)
{
ViewData["categories"] = from c in _dataContext.MovieCategories
select c;
ViewData["movies"] = from m in _dataContext.Movies
where m.CategoryId == id
select m;
return View();
}
}
}
請注意,Index() 和 Details() 動作都會新增兩個項目至檢視資料。 Index() 動作會新增兩個索引鍵:類別和電影。 類別索引鍵代表檢視主版頁面顯示的電影類別清單。 電影索引鍵代表 [索引檢視] 頁面顯示的電影清單。
Details() 動作也會新增兩個名為類別和電影的索引鍵。 同樣地,類別索引鍵代表檢視主版頁面顯示的電影類別清單。 電影索引鍵代表 [詳細資料] 檢視頁面中特定類別的電影清單 (請見圖 2)。
圖 02:詳細資料檢視 (按一下以檢視完整大小的影像)
索引檢視包含在清單 2 中。 其只會逐一查看檢視資料中電影項目代表的電影清單。
清單 2 – Views\Home\Index.aspx
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="MvcApplication1.Views.Home.Index" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<ul>
<% foreach (var m in (IEnumerable<Movie>)ViewData["movies"])
{ %>
<li><%= m.Title %></li>
<% } %>
</ul>
</asp:Content>
檢視主版頁面包含在清單 3 中。 檢視主版頁面會從檢視資料逐一查看並轉譯類別項目代表的所有電影類別。
清單 3 – Views\Shared\Site.master
<%@ Master Language="C#" AutoEventWireup="true" CodeBehind="Site.Master.cs" Inherits="MvcApplication1.Views.Shared.Site" %>
<%@ Import Namespace="MvcApplication1.Models" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title></title>
<asp:ContentPlaceHolder ID="head" runat="server">
</asp:ContentPlaceHolder>
</head>
<body>
<div>
<h1>My Movie Website</h1>
<% foreach (var c in (IEnumerable<MovieCategory>)ViewData["categories"])
{%>
<%= Html.ActionLink(c.Name, "Details", new {id=c.Id} ) %>
<% } %>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</body>
</html>
所有資料都會透過檢視資料傳遞至檢視和檢視主版頁面。 這是將資料傳遞至主版頁面的正確方式。
那麼,這個解決方案有什麼問題? 問題是,這個解決方案違反了 DRY (不重複原則)。 每個控制器動作都必須新增極為相同的電影類別清單,才能檢視資料。 在應用程式中產生重複的程式碼會讓應用程式變得更難維護、調整和修改。
好的解決方案
在本節中,我們會介紹另一種比較好的解決方案,將資料從控制器動作傳遞至檢視主版頁面。 我們只將電影類別新增至檢視資料一次,不用在每一個控制器動作中新增主版頁面的電影類別。 檢視主版頁面使用的所有檢視資料都會新增至應用程式的控制器。
ApplicationController 類別包含在清單 4 中。
清單 4 – Controllers\ApplicationController.cs
using System.Linq;
using System.Web.Mvc;
using MvcApplication1.Models;
namespace MvcApplication1.Controllers
{
public abstract class ApplicationController : Controller
{
private MovieDataContext _dataContext = new MovieDataContext();
public MovieDataContext DataContext
{
get { return _dataContext; }
}
public ApplicationController()
{
ViewData["categories"] = from c in DataContext.MovieCategories
select c;
}
}
}
在清單 4 中,有三件關於應用程式控制器的事項需要注意。 首先,請注意類別繼承自基底 System.Web.Mvc.Controller 類別。 應用程式控制器是控制器類別。
其次,請注意應用程式控制器類別是抽象類別。 抽象類別必須由具體類別實作。 由於應用程式控制器是抽象類別,因此您無法直接叫用該類別中定義的任何方法。 如果您嘗試直接叫用 Application 類別,會收到「找不到資源」的錯誤訊息。
第三請注意,應用程式控制器包含建構函式,該建構函式會新增電影類別清單至檢視資料。 繼承自應用程式控制器的每個控制器類別,都會自動呼叫應用程式控制器的建構函式。 每次在任何繼承自應用程式控制器的控制器呼叫任何動作時,檢視資料都將自動包含電影類別。
清單 5 中的電影控制器繼承自應用程式控制器。
清單 5 – Controllers\MoviesController.cs
using System.Linq;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class MoviesController : ApplicationController
{
/// <summary>
/// Show list of all movies
/// </summary>
public ActionResult Index()
{
ViewData["movies"] = from m in DataContext.Movies
select m;
return View();
}
/// <summary>
/// Show list of movies in a category
/// </summary>
public ActionResult Details(int id)
{
ViewData["movies"] = from m in DataContext.Movies
where m.CategoryId == id
select m;
return View();
}
}
}
如同上一節所討論的首頁控制器,電影控制器會公開兩個動作方法:Index()
和 Details()
。 請注意,檢視主版頁面顯示的電影類別清單不會新增至 Index()
或 Details()
方法中的檢視資料。 由於電控制器繼承自應用程式控制器,因此會自動新增電影類別清單至檢視資料。
請注意,這個新增檢視主版頁面檢視資料的解決方案不會違反 DRY (不重複原則)。 新增電影類別清單至檢視資料的程式碼只包含在一個位置內:應用程式控制器的建構函式。
摘要
在本教學課程中,我們討論了將檢視資料從控制器傳遞至檢視主版頁面的兩種方法。 首先,我們介紹簡單但難以維護的方法。 在第一節中,我們討論了如何在應用程式的每個控制器動作中,新增檢視主版頁面的檢視資料。 結論是這個方法不好,因為它違反 DRY (不重複原則)。
接下來,我們介紹了另一個更好的策略,可新增檢視主版頁面需要的資料以檢視資料。 只要在應用程式控制器內新增檢視資料一次,不用在每個控制器動作中新增檢視資料。 如此一來,將資料傳遞至 ASP.NET MVC 應用程式中的檢視主版頁面時,就可以避免程式碼重複。