使用控制器和视图实现列表/详细信息 UI

Microsoft

下载 PDF

这是免费 “NerdDinner”应用程序教程 的步骤 4,该教程介绍如何使用 ASP.NET MVC 1 生成小型但完整的 Web 应用程序。

步骤 4 演示如何向应用程序添加控制器,以利用我们的模型为用户提供 NerdDinner 站点上晚餐的数据列表/详细信息导航体验。

如果使用 ASP.NET MVC 3,建议遵循入门与 MVC 3MVC 音乐商店教程。

NerdDinner 步骤 4:控制器和视图

传统 Web 框架 (经典 ASP、PHP、ASP.NET Web Forms 等) ,传入 URL 通常映射到磁盘上的文件。 例如:对 URL(如“/Products.aspx”或“/Products.php”)的请求可能由“Products.aspx”或“Products.php”文件处理。

基于 Web 的 MVC 框架以略有不同的方式将 URL 映射到服务器代码。 它们不是将传入 URL 映射到文件,而是将 URL 映射到类上的方法。 这些类称为“控制器”,它们负责处理传入的 HTTP 请求、处理用户输入、检索和保存数据,以及确定响应以发送回客户端, (显示 HTML、下载文件、重定向到其他 URL 等) 。

现在,我们已为 NerdDinner 应用程序构建了一个基本模型,下一步是向应用程序添加控制器,以利用控制器为用户提供网站上的 Dinners 的数据列表/详细信息导航体验。

添加 DinnersController 控制器

首先,右键单击 Web 项目中的“控制器”文件夹,然后选择“ 添加>控制器 ”菜单命令 (也可以通过键入 Ctrl-M、Ctrl-C) 来执行此命令:

解决方案资源管理器窗口的屏幕截图,其中显示了“控制器”文件夹以及以蓝色突出显示的“添加”和“控制器”菜单项。

此时会显示“添加控制器”对话框:

“添加控制器”对话框的屏幕截图,其中显示了用文本 Dinners Controller 填充的“控制器名称”字段。

我们将新控制器命名为“DinnersController”,然后单击“添加”按钮。 然后,Visual Studio 将在 \Controllers 目录下添加 DinnersController.cs 文件:

“解决方案资源管理器”窗口的屏幕截图,其中以蓝色突出显示了“晚餐控制器”点 c 文件。

它还会在代码编辑器中打开新的 DinnersController 类。

将 Index () 和 Details () 操作方法添加到 DinnersController 类

我们希望访问者能够使用我们的应用程序浏览即将到来的晚餐列表,并允许他们单击列表中的任何晚餐以查看有关它的具体详细信息。 我们将通过从应用程序发布以下 URL 来执行此操作:

URL 用途
/晚餐/ 显示即将举行的晚餐的 HTML 列表
/Dinners/Details/[id] 显示由 URL 中嵌入的“id”参数指示的特定晚餐的详细信息, 该参数将与数据库中晚餐的 DinnerID 匹配。 例如:/Dinners/Details/2 将显示一个 HTML 页面,其中包含有关 Dinner 的详细信息,DinnerID 值为 2。

我们将通过向 DinnersController 类添加两个公共“操作方法”来发布这些 URL 的初始实现,如下所示:

public class DinnersController : Controller {

    //
    // HTTP-GET: /Dinners/

    public void Index() {
        Response.Write("<h1>Coming Soon: Dinners</h1>");
    }

    //
    // HTTP-GET: /Dinners/Details/2

    public void Details(int id) {
        Response.Write("<h1>Details DinnerID: " + id + "</h1>");
    }
}

然后,我们将运行 NerdDinner 应用程序,并使用浏览器调用它们。 键入 “/Dinners/” URL 将导致 Index () 方法运行,并发送回以下响应:

运行 NerdDinner 应用程序生成的响应窗口的屏幕截图,其中显示了文本即将推出:晚餐。

键入 “/Dinners/Details/2” URL 将导致 运行 Details () 方法,并发送回以下响应:

运行 NerdDinner 应用程序生成的响应窗口的屏幕截图,其中显示了文本“详细信息晚餐 ID:2”。

你可能想知道 - ASP.NET MVC 是如何知道创建 DinnersController 类并调用这些方法的? 为了了解这一点,让我们快速了解路由的工作原理。

了解 ASP.NET MVC 路由

ASP.NET MVC 包含功能强大的 URL 路由引擎,在控制 URL 映射到控制器类的方式方面提供了很大的灵活性。 它允许我们完全自定义 ASP.NET MVC 如何选择要创建哪个控制器类、对其调用哪个方法,以及配置可以从 URL/Querystring 自动分析变量并将其作为参数参数传递给方法的不同方式。 它可以灵活地针对 SEO (搜索引擎优化) 完全优化网站,以及从应用程序发布所需的任何 URL 结构。

默认情况下,新的 ASP.NET MVC 项目附带一组已注册的预配置的 URL 路由规则。 这使我们能够轻松地开始使用应用程序,而无需显式配置任何内容。 默认路由规则注册可以在项目的“Application”类中找到,可以通过双击项目的根目录中的“Global.asax”文件来打开该类:

解决方案资源管理器窗口的屏幕截图,其中显示了全局点 a x 文件,文件以蓝色突出显示,红色为圆圈。

默认 ASP.NET MVC 路由规则在此类的“RegisterRoutes”方法中注册:

public void RegisterRoutes(RouteCollection routes) {

    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default",                                       // Route name
        "{controller}/{action}/{id}",                    // URL w/ params
        new { controller="Home", action="Index",id="" }  // Param defaults
    );
}

“route.上面的 MapRoute () “方法调用注册一个默认路由规则,该规则使用 URL 格式将传入 URL 映射到控制器类:”/{controller}/{action}/{id}“ - 其中”controller“是要实例化的控制器类的名称,”action“是要在其上调用的公共方法的名称,而”id“是 URL 中嵌入的可选参数,可作为参数传递给方法。 传递给“MapRoute () ”方法调用的第三个参数是一组默认值,用于控制器/操作/id 值,如果它们不存在于 URL (Controller = “Home”, Action=“Index”, Id=“”) 。

下表演示了如何使用默认的“/{controllers}/{action}/{id}”路由规则映射各种 URL:

URL 控制器类 操作方法 传递的参数
/Dinners/Details/2 DinnersController id) 详细信息 ( id=2
/Dinners/Edit/5 DinnersController 编辑 (ID) id=5
/Dinners/Create DinnersController Create() 空值
/晚餐 DinnersController Index () 不可用
/家 HomeController Index () 空值
/ HomeController Index () 不可用

最后三行显示默认值 (Controller = Home, Action = Index, Id = “”) 正在使用。 由于“Index”方法注册为默认操作名称(如果未指定),因此“/Dinners”和“/Home”URL 会导致在其控制器类上调用 Index () 操作方法。 由于“主”控制器注册为默认控制器(如果未指定),因此“/”URL 会导致创建 HomeController,并调用其上的 Index () 操作方法。

如果不喜欢这些默认 URL 路由规则,好消息是它们易于更改 - 只需在上面的 RegisterRoutes 方法中编辑它们即可。 不过,对于 NerdDinner 应用程序,我们不会更改任何默认 URL 路由规则,而是按原样使用它们。

使用 DinnersController 中的 DinnerRepository

现在,让我们将 DinnersController 的 Index () 和 Details () 操作方法的当前实现替换为使用我们模型的实现。

我们将使用之前生成的 DinnerRepository 类来实现该行为。 首先,我们将添加引用“NerdDinner.Models”命名空间的“using”语句,然后将 DinnerRepository 的实例声明为 DinnerController 类上的字段。

在本章的后面部分,我们将介绍“依赖关系注入”的概念,并展示另一种方法,让我们的控制器获取对 DinnerRepository 的引用,该引用可实现更好的单元测试 - 但现在,我们将只创建一个 DinnerRepository 的内联实例,如下所示。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Models;

namespace NerdDinner.Controllers {

    public class DinnersController : Controller {

        DinnerRepository dinnerRepository = new DinnerRepository();

        //
        // GET: /Dinners/

        public void Index() {
            var dinners = dinnerRepository.FindUpcomingDinners().ToList();
        }

        //
        // GET: /Dinners/Details/2

        public void Details(int id) {
            Dinner dinner = dinnerRepository.GetDinner(id);
        }
    }
}

现在,我们已准备好使用检索到的数据模型对象生成返回的 HTML 响应。

将视图与控制器配合使用

虽然可以在操作方法中编写代码来汇编 HTML,然后使用 Response.Write () 帮助程序方法将其发送回客户端,但这种方法很快就会变得相当笨重。 更好的方法是,我们只在 DinnersController 操作方法中执行应用程序和数据逻辑,然后将呈现 HTML 响应所需的数据传递给负责输出 HTML 表示形式的单独“视图”模板。 稍后我们将看到,“视图”模板是一个文本文件,通常包含 HTML 标记和嵌入式呈现代码的组合。

将控制器逻辑与视图呈现分开可带来几个重大好处。 具体而言,它有助于在应用程序代码和 UI 格式设置/呈现代码之间强制实施明确的“关注点分离”。 这使得与 UI 呈现逻辑隔离的单元测试应用程序逻辑要容易得多。 这样,以后无需更改应用程序代码即可更轻松地修改 UI 呈现模板。 它可以让开发人员和设计人员更轻松地协作处理项目。

我们可以更新 DinnersController 类,以指示我们希望使用视图模板发送回 HTML UI 响应,方法是将两个操作方法的方法签名从返回类型“void”改为返回类型为“ActionResult”。 然后,我们可以调用 Controller 基类上的 View () 帮助程序方法,以返回“ViewResult”对象,如下所示:

public class DinnersController : Controller {

    DinnerRepository dinnerRepository = new DinnerRepository();

    //
    // GET: /Dinners/

    public ActionResult Index() {

        var dinners = dinnerRepository.FindUpcomingDinners().ToList();

        return View("Index", dinners);
    }

    //
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View("Details", dinner);
    }
}

上面使用的 View () 帮助程序方法的签名如下所示:

视图帮助程序方法的屏幕截图,其中文本为“查看结果视图 (字符串视图名称、对象模型) 。

View () 帮助程序方法的第一个参数是要用于呈现 HTML 响应的视图模板文件的名称。 第二个参数是一个模型对象,其中包含视图模板呈现 HTML 响应所需的数据。

在 Index () 操作方法中,我们将调用 View () 帮助程序方法,并指示我们希望使用“索引”视图模板呈现晚餐的 HTML 列表。 我们将向视图模板传递一系列 Dinner 对象,以便从中生成列表:

//
    // GET: /Dinners/

    public ActionResult Index() {
    
        var dinners = dinnerRepository.FindUpcomingDinners().ToList();
        
        return View("Index", dinners);
    }

在 Details () 操作方法中,我们尝试使用 URL 中提供的 ID 检索 Dinner 对象。 如果找到有效的 Dinner,我们将调用 View () 帮助程序方法,指示要使用“详细信息”视图模板来呈现检索到的 Dinner 对象。 如果请求的晚餐无效,我们将呈现一条有用的错误消息,指示 Dinner 不存在使用“NotFound”视图模板 (和重载版本的 View () 帮助程序方法,该方法仅采用模板名称) :

//
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.FindDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View("Details", dinner);
    }

现在,让我们实现“NotFound”、“详细信息”和“索引”视图模板。

实现“NotFound”视图模板

我们将首先实现“NotFound”视图模板,该模板显示一条友好的错误消息,指示找不到请求的晚餐。

我们将通过将文本光标定位在控制器操作方法中来创建新的视图模板,然后右键单击并选择“添加视图”菜单命令, (也可以通过键入 Ctrl-M、Ctrl-V) 来执行此命令:

项目屏幕截图,右键单击菜单项“添加视图”以蓝色突出显示,红色圆圈。

此时会显示如下所示的“添加视图”对话框。 默认情况下,对话框将预先填充要创建的视图的名称,以匹配启动对话时游标位于的操作方法的名称 (在本例中为“Details”) 。 由于我们想要首先实现“NotFound”模板,因此我们将重写此视图名称并将其设置为“NotFound”:

“添加视图”窗口的屏幕截图,其中“视图名称”字段设置为“未找到”,选中了“选择母版页”框,并将“内容位置持有者 ID”设置为“主内容”。

单击“添加”按钮时,Visual Studio 将在“\Views\Dinners”目录中为我们创建新的“NotFound.aspx”视图模板 (如果该目录尚不存在) 也会创建该模板:

解决方案资源管理器窗口文件夹层次结构的屏幕截图,其中以蓝色突出显示了“找不到”点 a s p x 文件。

它还会在代码编辑器中打开新的“NotFound.aspx”视图模板:

代码编辑器窗口的屏幕截图,其中“找不到”点在代码编辑器中打开了 s p x 文件。

默认情况下,视图模板有两个“内容区域”,我们可以在其中添加内容和代码。 第一个允许我们自定义发送回的 HTML 页面的“标题”。 第二个允许我们自定义发送回的 HTML 页面的“main内容”。

若要实现“NotFound”视图模板,我们将添加一些基本内容:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Dinner Not Found
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Dinner Not Found</h2>

    <p>Sorry - but the dinner you requested doesn't exist or was deleted.</p>

</asp:Content>

然后,我们可以在浏览器中试用它。 为此,请请求 “/Dinners/Details/9999” URL。 这将引用数据库中当前不存在的 dinner,并将导致 DinnersController.Details () 操作方法呈现“NotFound”视图模板:

“我的 MVC 应用程序”窗口的屏幕截图,地址框中的/Dinners/Details/9999 U R L 以红色圈出。

在上面的屏幕截图中,你会注意到,基本视图模板继承了一组围绕屏幕上main内容的 HTML。 这是因为视图模板使用“母版页”模板,使我们可以在网站上的所有视图上应用一致的布局。 在本教程的后面部分,我们将讨论母版页如何工作。

实现“详细信息”视图模板

现在,让我们实现“详细信息”视图模板 - 它将为单个 Dinner 模型生成 HTML。

为此,我们将文本光标定位在“详细信息”操作方法中,然后右键单击并选择“添加视图”菜单命令 (或按 Ctrl-M、Ctrl-V) :

代码编辑器窗口的屏幕截图,其中显示了以红色突出显示的右键单击菜单项“添加视图点点点”。

此时会显示“添加视图”对话框。 我们将保留默认视图名称 (“Details”) 。 我们还将在对话框中选中“创建强类型视图”复选框,并使用组合框下拉列表选择 () 从控制器传递到视图的模型类型的名称。 对于此视图,我们将传递 Dinner 对象 (此类型的完全限定名称为:“NerdDinner.Models.Dinner”) :

“添加视图”窗口的屏幕截图,其中“查看内容”下拉列表设置为“详细信息”,“视图”数据类型设置为“Nerd Dinner 点模型点 Dinner”。

与之前选择创建“空视图”的模板不同,这次我们将选择使用“详细信息”模板自动“搭建”视图。 可以通过更改上述对话框中的“查看内容”下拉列表来指示这一点。

“基架”将基于传递给它的 Dinner 对象生成详细信息视图模板的初始实现。 这提供了一种简单的方法来快速开始实现视图模板。

单击“添加”按钮时,Visual Studio 将在“\Views\Dinners”目录中为我们创建新的“Details.aspx”视图模板文件:

解决方案资源管理器窗口的屏幕截图,其中显示了文件夹层次结构,其中以蓝色突出显示了 Dinners 文件夹。

它还会在代码编辑器中打开新的“Details.aspx”视图模板。 它将包含基于 Dinner 模型的详细信息视图的初始基架实现。 基架引擎使用 .NET 反射来查看在传递它的类上公开的公共属性,并根据它找到的每种类型添加适当的内容:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Details
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Details</h2>

    <fieldset>
        <legend>Fields</legend>
        <p>
            DinnerID:
            <%=Html.Encode(Model.DinnerID) %>
        </p>
        <p>
            Title:
            <%=Html.Encode(Model.Title) %>
        </p>
        <p>
            EventDate:
            <%= Html.Encode(String.Format("{0:g}", Model.EventDate)) %>
        </p>
        <p>
            Description:
            <%=Html.Encode(Model.Description) %>
        </p>
        <p>
            HostedBy:
            <%=Html.Encode(Model.HostedBy) %>
        </p>
        <p>
            ContactPhone:
            <%=Html.Encode(Model.ContactPhone) %>
        </p>
        <p>
            Address:
            <%=Html.Encode(Model.Address) %>
        </p>
        <p>
            Country:
            <%=Html.Encode(Model.Country) %>
        </p>
        <p>
            Latitude:
            <%= Html.Encode(String.Format("{0:F}",Model.Latitude)) %>
        </p>
        <p>
            Longitude:
            <%= Html.Encode(String.Format("{0:F}",Model.Longitude)) %>
        </p>
    </fieldset>
    
    <p>
        <%=Html.ActionLink("Edit","Edit", new { id=Model.DinnerID }) %>|
        <%=Html.ActionLink("Back to List", "Index") %>
    </p>
    
</asp:Content>

我们可以请求 “/Dinners/Details/1” URL,以查看此“details”基架实现在浏览器中的外观。 使用此 URL 将显示我们在首次创建数据库时手动添加到数据库的晚餐之一:

应用程序响应窗口的屏幕截图,其中显示地址框中以红色圆圈的/Dinners/Details/1 U R L。

这使我们能够快速启动并运行,并为我们提供 Details.aspx 视图的初始实现。 然后,我们可以对其进行调整,以自定义 UI,使其满意。

当我们更仔细地查看 Details.aspx 模板时,会发现它包含静态 HTML 以及嵌入的呈现代码。 <% 代码> 块在视图模板呈现时执行代码, <%= %> 代码块执行其中包含的代码,然后将结果呈现到模板的输出流。

我们可以在 View 中编写代码来访问使用强类型“Model”属性从控制器传递的“Dinner”模型对象。 在编辑器中访问此“模型”属性时,Visual Studio 为我们提供了完整的代码 intellisense:

代码编辑器窗口的屏幕截图,其中显示了一个下拉列表,其中项“说明”以蓝色突出显示。

让我们进行一些调整,以便最终的“详细信息”视图模板的源如下所示:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Dinner: <%=Html.Encode(Model.Title) %>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2><%=Html.Encode(Model.Title) %></h2>
    <p>
        <strong>When:</strong> 
        <%=Model.EventDate.ToShortDateString() %> 

        <strong>@</strong>
        <%=Model.EventDate.ToShortTimeString() %>
    </p>
    <p>
        <strong>Where:</strong> 
        <%=Html.Encode(Model.Address) %>,
        <%=Html.Encode(Model.Country) %>
    </p>
     <p>
        <strong>Description:</strong> 
        <%=Html.Encode(Model.Description) %>
    </p>       
    <p>
        <strong>Organizer:</strong> 
        <%=Html.Encode(Model.HostedBy) %>
        (<%=Html.Encode(Model.ContactPhone) %>)
    </p>
    
    <%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID })%> |
    <%= Html.ActionLink("Delete Dinner","Delete", new { id=Model.DinnerID})%>   
     
</asp:Content>

当我们再次访问 “/Dinners/Details/1” URL 时,它将呈现如下:

应用程序响应窗口的屏幕截图,其中显示了 dot NET Futures 视图的新样式。

实现“索引”视图模板

现在,让我们实现“索引”视图模板 - 它将生成即将推出的 Dinners 的列表。 为此,我们将文本光标置于 Index 操作方法中,然后右键单击并选择“添加视图”菜单命令 (或按 Ctrl-M、Ctrl-V) 。

在“添加视图”对话框中,我们将保留名为“索引”的视图模板,并选中“创建强类型视图”复选框。 这一次,我们将选择自动生成“列表”视图模板,并选择“NerdDinner.Models.Dinner”作为传递给视图 (模型类型,由于我们指示要创建“列表”基架,因此“添加视图”对话框假定我们将一系列 Dinner 对象从控制器传递到视图) :

“添加视图”窗口的屏幕截图,其中“视图名称”设置为“索引”,勾选了“创建强类型视图”框,并勾选了“选择母版页”框。

单击“添加”按钮时,Visual Studio 将在“\Views\Dinners”目录中为我们创建新的“Index.aspx”视图模板文件。 它将在其内“搭建基架”初始实现,该实现提供我们传递给视图的 Dinners 的 HTML 表列表。

当我们运行应用程序并访问 “/Dinners/” URL 时,它将呈现晚餐列表,如下所示:

应用程序响应窗口的屏幕截图,其中显示了“添加视图”更新后网格布局中的晚餐列表。

上面的表解决方案为我们提供了一个类似网格的晚餐数据布局 - 这不是我们想要的消费者面对晚餐列表。 我们可以更新 Index.aspx 视图模板并对其进行修改以列出更少的数据列,并使用 <ul> 元素来呈现它们,而不是使用以下代码呈现表:

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Upcoming Dinners</h2>

    <ul>
        <% foreach (var dinner in Model) { %>
        
            <li>                 
                <%=Html.Encode(dinner.Title) %>            
                on 
                <%=Html.Encode(dinner.EventDate.ToShortDateString())%>
                @
                <%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
            </li>
            
        <% } %>
    </ul>
    
</asp:Content>

我们在上述 foreach 语句中使用“var”关键字 (keyword) ,因为我们循环访问模型中的每个晚餐。 不熟悉 C# 3.0 的用户可能会认为使用“var”意味着 dinner 对象是后期绑定的。 相反,这意味着编译器对强类型“Model”属性使用类型推理, (该属性的类型为“IEnumerable<Dinner>”) 并将本地“dinner”变量编译为 Dinner 类型 , 这意味着我们在代码块中获取完整的智能感知和编译时检查:

代码编辑器窗口的屏幕截图,其中显示了一个下拉菜单,其中灰色虚线框中突出显示了“地址”列表项。

当我们在浏览器中的 /Dinners URL 上点击刷新时,更新后的视图现在如下所示:

应用程序响应窗口的屏幕截图,其中显示了刷新命令后即将举行的晚餐列表。

这看起来更好了,但还不完全存在。 最后一步是使最终用户能够单击列表中的单个 Dinners 并查看有关它们的详细信息。 我们将通过呈现链接到 DinnersController 上的 Details 操作方法的 HTML 超链接元素来实现此目标。

可以通过以下两种方式之一在索引视图中生成这些超链接。 第一种是手动创建 HTML <元素,如下所示,我们在 HTML 元素中><嵌入 <% %>> 块:

代码编辑器窗口的屏幕截图,其中类和百分比块文本突出显示并用红色圆圈。

我们可以使用的替代方法是利用 ASP.NET MVC 中的内置“Html.ActionLink () ”帮助程序方法,该方法支持以编程方式创建链接到控制器上另一>个操作方法的 HTML <元素:

<%= Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>

Html.ActionLink () 帮助程序方法的第一个参数是用于显示 (在此示例中为 dinner) 的标题的链接文本,第二个参数是我们要生成链接的控制器操作名称,在本例中 (,详细信息方法) ,第三个参数是一组要发送到操作的参数 (实现为具有属性名称/值的匿名类型) 。 在这种情况下,我们将指定要链接到的 dinner 的“id”参数,由于 ASP.NET MVC 中的默认 URL 路由规则为“{Controller}/{Action}/{id}”,Html.ActionLink () 帮助程序方法将生成以下输出:

<a href="/Dinners/Details/1">.NET Futures</a>

对于 Index.aspx 视图,我们将使用 Html.ActionLink () 帮助程序方法,并在列表中提供指向相应详细信息 URL 的晚餐链接:

<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Upcoming Dinners
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Upcoming Dinners</h2>

    <ul>
        <% foreach (var dinner in Model) { %>
        
            <li>     
                <%=Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>
                on 
                <%=Html.Encode(dinner.EventDate.ToShortDateString())%>
                @
                <%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
            </li>
            
        <% } %>
    </ul>
</asp:Content>

现在,当我们点击 /Dinners URL 时,晚餐列表如下所示:

应用程序响应窗口的屏幕截图,其中显示了即将推出的晚餐列表,其中包含与列表项对应的新链接。

单击列表中的任一“晚餐”时,我们将导航以查看其详细信息:

应用程序响应窗口的屏幕截图,其中显示了输入到数据库中的所选列表项及其对应的详细信息。

基于约定的命名和 \Views 目录结构

ASP.NET MVC 应用程序在解析视图模板时默认使用基于约定的目录命名结构。 这样,开发人员就可以避免在从 Controller 类中引用视图时必须完全限定位置路径。 默认情况下,ASP.NET MVC 将在应用程序下方的 *\Views[ControllerName]* 目录中查找视图模板文件。

例如,我们一直在研究 DinnersController 类 - 它显式引用三个视图模板:“Index”、“Details”和“NotFound”。 ASP.NET MVC 默认会在应用程序根目录下的 \Views\Dinners 目录中查找这些视图:

解决方案资源管理器窗口的屏幕截图,其中显示了文件夹层次结构,蓝色矩形中突出显示了 Dinners 文件夹。

请注意,项目目前有三个控制器类, (DinnersController、HomeController 和 AccountController – 在创建项目) 时默认添加后,有三个子目录 (\Views 目录中每个控制器) 一个。

从主控制器和帐户控制器引用的视图将自动解析其视图模板从相应的 \Views\Home\Views\Account 目录。 \Views\Shared 子目录提供了一种存储视图模板的方法,这些模板可在应用程序中的多个控制器之间重用。 当 ASP.NET MVC 尝试解析视图模板时,它将首先在 \Views[Controller] 特定目录中检查,如果它找不到该视图模板,它将在 \Views\Shared 目录中查找。

在命名单个视图模板时,建议的指导是让视图模板与导致它呈现的操作方法共享同名。 例如,上述“Index”操作方法使用“索引”视图呈现视图结果,“Details”操作方法使用“详细信息”视图呈现其结果。 这样就可以轻松快速查看与每个操作关联的模板。

当视图模板的名称与在控制器上调用的操作方法同名时,开发人员无需显式指定视图模板名称。 相反,我们可以将模型对象传递到“View () ”帮助程序方法 (而不指定视图名称) ,ASP.NET MVC 将自动推断我们希望使用磁盘上的 \Views[ControllerName][ActionName] 视图模板来呈现它。

这使我们能够稍微清理控制器代码,并避免在代码中重复两次名称:

public class DinnersController : Controller {

    DinnerRepository dinnerRepository = new DinnerRepository();

    //
    // GET: /Dinners/

    public ActionResult Index() {

        var dinners = dinnerRepository.FindUpcomingDinners().ToList();

        return View(dinners);
    }

    //
    // GET: /Dinners/Details/2

    public ActionResult Details(int id) {

        Dinner dinner = dinnerRepository.GetDinner(id);

        if (dinner == null)
            return View("NotFound");
        else
            return View(dinner);
    }
}

上述代码是实现网站良好的晚餐列表/详细信息体验所需的全部内容。

下一步

我们现在构建了一个漂亮的晚餐浏览体验。

现在,让我们启用 CRUD (创建、读取、更新、删除) 数据表单编辑支持。