向视图母版页传递数据 (C#)

Microsoft

下载 PDF

本教程的目的是说明如何将数据从控制器传递到视图母版页。 我们将探讨将数据传递到视图母版页的两种策略。 首先,我们讨论一个简单的解决方案,它会导致难以维护的应用程序。 接下来,我们将研究一个更好的解决方案,该解决方案需要稍微多一点的初始工作,但会产生更易于维护的应用程序。

将数据传递到查看母版页

本教程的目的是说明如何将数据从控制器传递到视图母版页。 我们将探讨将数据传递到视图母版页的两种策略。 首先,我们讨论一个简单的解决方案,它会导致难以维护的应用程序。 接下来,我们将研究一个更好的解决方案,该解决方案需要稍微多一点的初始工作,但会产生更易于维护的应用程序。

问题

假设要生成电影数据库应用程序,并且想要在应用程序的每个页面上显示电影类别列表 (请参阅图 1) 。 此外,假设电影类别列表存储在数据库表中。 在这种情况下,从数据库中检索类别并在视图母版页中呈现电影类别列表是有意义的。

在视图母版页中显示电影类别

图 01:在视图母版页中显示电影类别 (单击以查看全尺寸图像)

下面是问题。 如何在母版页中检索电影类别列表? 直接在母版页中调用模型类的方法很诱人。 换句话说,在母版页中包含用于从数据库检索数据的代码很诱人。 但是,绕过 MVC 控制器访问数据库会违反关注点的干净分离,这是生成 MVC 应用程序的主要优势之一。

在 MVC 应用程序中,你希望 MVC 视图与 MVC 模型之间的所有交互都由 MVC 控制器处理。 这种关注点分离可生成更易于维护、更适应性和可测试的应用程序。

在 MVC 应用程序中,传递给视图的所有数据(包括视图母版页)都应通过控制器操作传递到视图。 此外,应利用视图数据传递数据。 在本教程的其余部分,我将探讨将视图数据传递到视图母版页的两种方法。

简单解决方案

让我们从将视图数据从控制器传递到视图母版页的最简单解决方案开始。 最简单的解决方案是在每个控制器操作中传递母版页的视图数据。

请考虑清单 1 中的控制器。 它公开了两个名为 Index()Details()的操作。 Index()操作方法返回 Movies 数据库表中的每个电影。 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 () 操作添加了两个键:类别和电影。 类别键表示视图母版页显示的电影类别列表。 movies 键表示“索引视图”页显示的电影列表。

Details () 操作还添加了两个名为 categories 和 movies 的键。 类别键再次表示视图母版页显示的电影类别列表。 movies 键表示“详细信息”视图页面显示的特定类别中的电影列表 (请参阅图 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 中的 Movies 控制器继承自应用程序控制器。

列表 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();
          }

     }
}

电影控制器与上一部分中讨论的 Home 控制器一样,公开了两个名为 Index()Details()的操作方法。 请注意,视图母版页显示的电影类别列表不会添加到 或 Details() 方法中的Index()查看数据。 由于 Movies 控制器继承自应用程序控制器,因此会自动添加电影类别列表以查看数据。

请注意,此为视图母版页添加视图数据的解决方案并不违反 DRY (不要重复) 原则。 用于添加电影类别列表以查看数据的代码仅包含在一个位置:应用程序控制器的构造函数。

总结

在本教程中,我们讨论了将视图数据从控制器传递到视图母版页的两种方法。 首先,我们研究了一种简单但难以维护的方法。 在第一部分中,我们讨论了如何在应用程序中的每个控制器操作中为视图母版页添加视图数据。 我们的结论是,这是一个糟糕的方法,因为它违反了干 (不要重复自己) 原则。

接下来,我们研究了一种更好的策略,用于添加视图母版页所需的数据来查看数据。 我们不在每个控制器操作中添加视图数据,而是在应用程序控制器中只添加了一次视图数据。 这样,就可以避免将数据传递到 ASP.NET MVC 应用程序中的视图母版页时出现重复代码。