Pasar datos a las páginas maestras de vista (C#)

de Microsoft

Descargar PDF

El objetivo de este tutorial es explicar cómo puede pasar datos de un controlador a una página maestra de vista. Se examinarán dos estrategias para pasar datos a una página maestra de vista. En primer lugar, se describe una solución sencilla que da lugar a una aplicación que es difícil de mantener. A continuación, se examina una solución mucho mejor que requiere un poco más de trabajo inicial, pero da como resultado una aplicación mucho más fácil de mantener.

Transferencia de datos a páginas maestras de vista

El objetivo de este tutorial es explicar cómo puede pasar datos de un controlador a una página maestra de vista. Se examinarán dos estrategias para pasar datos a una página maestra de vista. En primer lugar, se describe una solución sencilla que da lugar a una aplicación que es difícil de mantener. A continuación, se examina una solución mucho mejor que requiere un poco más de trabajo inicial, pero da como resultado una aplicación mucho más fácil de mantener.

El problema

Imagine que va a crear una aplicación de base de datos de películas y quiere mostrar la lista de categorías de películas en cada página de la aplicación (vea la figura 1). Imagine, además, que la lista de categorías de películas se almacena en una tabla de base de datos. En ese caso, tendría sentido recuperar las categorías de la base de datos y representar la lista de categorías de películas dentro de una página maestra de vista.

Displaying movie categories in a view master page

Figura 01: Presentación de categorías de películas en una página maestra de vista (Haga clic para ver la imagen a tamaño completo)

Este es el problema. ¿Cómo se recupera la lista de categorías de películas en la página maestra? Es tentador llamar directamente a métodos de las clases de modelo directamente en la página maestra. Es decir, es tentador incluir el código para recuperar los datos de la base de datos directamente en la página maestra. Pero si omite los controladores de MVC para acceder a la base de datos infringiría la separación limpia de preocupaciones que es una de las principales ventajas de crear una aplicación MVC.

En una aplicación MVC, quiere que los controladores de MVC controlen toda la interacción entre las vistas y el modelo de MVC. Esta separación de preocupaciones da como resultado una aplicación más fácil de mantener, adaptar y probar.

En una aplicación MVC, todos los datos pasados a una vista (incluida una página maestra de vista) se deben pasar mediante una acción del controlador. Además, los datos se deben pasar aprovechando los datos de vista. En el resto de este tutorial, se examinarán dos métodos para pasar datos de vista a una página maestra de vista.

La solución sencilla

Comenzará con la solución más sencilla para pasar datos de vista de un controlador a una página maestra de vista. La solución más sencilla consiste en pasar los datos de vista de la página maestra en cada acción del controlador.

Considere el controlador de la lista 1. Expone dos acciones denominadas Index() y Details(). El método de acción Index() devuelve todas las películas de la tabla de base de datos Movies. El método de acción Details() devuelve todas las películas de una categoría de película determinada.

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

Observe que las acciones Index() y Details() agregan dos elementos para ver los datos. La acción Index() agrega dos claves: categories y movies. La clave categories representa la lista de categorías de películas mostradas por la página maestra de vista. La clave movies representa la lista de películas mostradas por la página de vista de índice.

La acción Details() también agrega dos claves denominadas categories y movies. Como antes, la clave categories representa la lista de categorías de películas mostradas por la página maestra de vista. La clave movies representa la lista de películas de una categoría determinada mostrada por la página de vista de detalles (vea la figura 2).

The Details view

Figura 02: Vista Details (Haga clic para ver la imagen a tamaño completo)

La vista Index se muestra en la lista 2. Simplemente itera por la lista de películas representadas por el elemento movies en los datos de vista.

Lista 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>

La página maestra de vista se muestra en la lista 3. La página maestra de vista itera por todas las categorías de películas representadas por el elemento categories de los datos de vista.

Lista 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>

Todos los datos se pasan a la vista y a la página maestra de vista mediante datos de vista. Es la manera correcta de pasar datos a la página maestra.

Por tanto, ¿cuál es el problema de esta solución? El problema es que esta solución infringe el principio DRY (Una vez y solo una). Cada acción del controlador debe agregar la misma lista de categorías de películas a los datos de la vista. Tener código duplicado en la aplicación hace que sea mucho más difícil de mantener, adaptar y modificar.

La solución correcta

En esta sección, se examina una solución alternativa y mejor para pasar datos de una acción del controlador a una página maestra de vista. En lugar de agregar las categorías de películas para la página maestra en cada acción del controlador, solo se agregan una vez las categorías de películas a los datos de vista. Todos los datos de vista usados por la página maestra de vista se agregan en un controlador Application.

La clase ApplicationController se muestra en la lista 4.

Lista 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;
          }

     }
}

Hay tres aspectos que debe tener en cuenta sobre el controlador Application de la lista 4. En primer lugar, observe que la clase hereda de la clase base System.Web.Mvc.Controller. El controlador Application es una clase de controlador.

En segundo lugar, observe que la clase de controlador Application es una clase abstracta. Una clase abstracta es la que se debe implementar mediante una clase concreta. Como el controlador Application es una clase abstracta, no puede invocar ningún método definido en la clase directamente. Si intenta invocar la clase Application directamente, recibirá un mensaje de error Resource Cannot Be Found.

En tercer lugar, observe que el controlador Application contiene un constructor que agrega la lista de categorías de películas a los datos de vista. Toda clase de controlador que hereda del controlador Application llama automáticamente al constructor del controlador Application. Siempre que llame a cualquier acción en cualquier controlador que herede del controlador Application, las categorías de películas se incluyen automáticamente en los datos de vista.

El controlador Movies de la lista 5 hereda del controlador Application.

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

     }
}

El controlador Movies, al igual que el controlador Home descrito en la sección anterior, expone dos métodos de acción denominados Index() y Details(). Observe que la lista de categorías de películas mostradas por la página maestra de vista no se agrega a los datos de vista en el método Index() ni Details(). Como el controlador Movies hereda del controlador Application, la lista de categorías de películas se agrega automáticamente a los datos de vista.

Tenga en cuenta que esta solución para agregar datos de vista para una página maestra de vista no infringe el principio DRY (Una vez y solo una). El código para agregar la lista de categorías de películas a los datos de vista solo se encuentra en una ubicación: el constructor para el controlador Application.

Resumen

En este tutorial, se han analizado dos enfoques para pasar datos de vista de un controlador a una página maestra de vista. En primer lugar, se ha examinado un enfoque sencillo pero difícil de mantener. En la primera sección, se ha explicado cómo puede agregar datos de vista para una página maestra de vista en cada acción del controlador de la aplicación. Se ha concluido que era un enfoque incorrecto porque infringe el principio DRY (Una vez y solo una).

A continuación, se ha examinado una estrategia mucho mejor para agregar datos requeridos por una página maestra de vista a los datos de vista. En lugar de agregar los datos de vista en cada acción del controlador, solo se agregan una vez dentro de un controlador Application. De este modo, puede evitar el código duplicado al pasar datos a una página maestra de vista en una aplicación ASP.NET MVC.