Asp.Net MVC 2.0 动态加载Master Page数据(一)
时间:2011-03-04 来源:RyanBell
传递数据到母版页
这篇教程的目的是解释你可以怎样将数据从一个控制器传递到一个视图母版页。我们测试了两种传递数据到母版页的策略。首先,我们讨论了一个容易的方案,但这个方案导致应用难以维护。接下来,我们测试了一个比较好的解决方案。它需要多一点的早期工作,但是会产生更容易点维护的应用。
问题
想象下你正在创建一个电影数据库应用,然后你想要在应用的每一页都显示一个电影分类列表(如图1)。此外,这个电影分类表存储在数据库表里。因此,从数据库检索这些分类然后在一个视图母版页面里显示这些电影分类显然很有意义。
图1:在视图母版页显示电影分类
问题出来了。在视图母版页里你怎么检索电影分类列表?直接在母版页调用模型类的方法显然很诱人。换句话说,在你的模板页里检索数据库的数据是很诱人的。然而,绕过你的MVC控制器去获得接触数据库会违反干净的关注点分离--这个MVC应用最主要的优点之一。
在一个MVC应用里,你会想要所有的MVC视图和MVC模型的交换都由MVC控制器来处理。这种关注点的分离意味着一个更可维护的、适应性更强并且测试性更强的应用。
在一个MVC应用,所有的数据传递到一个视图--包括视图母版页--都应该由控制器行为传递到视图。此外,数据应该利用视图数据的优势来传递。在这篇教程的其余部分,我测试了传递数据到视图母版页的两种方法。
简单的解决方案
让我们从一个简单的解决方案开始。这个简单的解决方案是在每一个控制器行为里传递视图数据到母版页。
考虑下清单1的控制器。它暴露了名叫Index()和Detials() 的两个行为。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()行为添加了两个键:categories和movies。Categories键。Movies键代表着由Index视图也显示的电影列表。
Details()行为同时也添加了两个名叫categories和movies的键。Categories键,再一次,代表着由视图母版页显示的电影分类。Movies键代表着由Details视图页面显示的特定的分类(如图2)。
图2:Details视图
Index视图被包含在清单2.它简单地迭代了在视图数据里的由movies项表示的电影列表。
清单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.视图母版页迭代并且渲染了从视图数据获得的categories项表示的所有电影分类。
清单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(Don't Repeat Yourself)原则。每一个控制器行为必须添加同样的电影分类列表到视图数据。应用里有重复的代码会让你的应用维护难度更大、适应性更差并且修改也更困难。