Parte 4: Modelos y acceso a datos
por Jon Galloway
MVC Music Store es una aplicación de tutorial que presenta y explica paso a paso cómo usar ASP.NET MVC y Visual Studio para el desarrollo web.
MVC Music Store es una implementación ligera de una tienda de ejemplo en la que se venden álbumes de música en línea e implementa la administración básica del sitio, el inicio de sesión de usuario y la funcionalidad del carro de la compra.
En esta serie de tutoriales se detallan todos los pasos realizados para compilar la aplicación de ejemplo ASP.NET MVC Music Store. En la parte 4 se tratan modelos y acceso a datos.
Hasta ahora, hemos pasado "datos ficticios" de nuestros controladores a nuestras plantillas de vista. Ahora estamos listos para enlazar una base de datos real. En este tutorial se explica cómo usar SQL Server Compact Edition (a menudo denominado SQL CE) como motor de base de datos. SQL CE es una base de datos gratuita, insertada y basada en archivos que no requiere ninguna instalación o configuración, lo que facilita mucho el desarrollo local.
Acceso a la base de datos con Entity Framework Code-First
Usaremos la compatibilidad de Entity Framework (EF) que se incluye en proyectos ASP.NET MVC 3 para consultar y actualizar la base de datos. EF es una API de datos de asignación relacional de objetos (ORM) flexible que permite a los desarrolladores consultar y actualizar datos almacenados en una base de datos de forma orientada a objetos.
Entity Framework, versión 4 admite un paradigma de desarrollo denominado code-first. Code-first permite crear objetos de modelo escribiendo clases simples (también conocidas como POCO de objetos CLR "sin formato" antiguos) e incluso puede crear la base de datos sobre la marcha a partir de las clases.
Cambios en nuestras clases de modelo
Aprovecharemos la característica de creación de bases de datos en Entity Framework en este tutorial. Sin embargo, antes de hacerlo, vamos a realizar algunos cambios menores en nuestras clases de modelo para agregar algunas cosas que usaremos más adelante.
Adición de las clases de modelo de Artista
Nuestros álbumes se asociarán con Artistas, por lo que agregaremos una clase de modelo simple para describir un artista. Agregue una nueva clase a la carpeta Models denominada Artist.cs con el código que se muestra a continuación.
namespace MvcMusicStore.Models
{
public class Artist
{
public int ArtistId { get; set; }
public string Name { get; set; }
}
}
Actualización de nuestras clases de modelo
Actualice la clase Album como se muestra a continuación.
namespace MvcMusicStore.Models
{
public class Album
{
public int AlbumId { get; set; }
public int GenreId { get; set; }
public int ArtistId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public Genre Genre { get; set; }
public Artist Artist { get; set; }
}
}
A continuación, realice las siguientes actualizaciones de la clase Género.
using System.Collections.Generic;
namespace MvcMusicStore.Models
{
public partial class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Album> Albums { get; set; }
}
}
Adición de la carpeta App_Data
Agregaremos un directorio App_Data al proyecto para almacenar los archivos de base de datos de SQL Server Express. App_Data es un directorio especial en ASP.NET que ya tiene los permisos de acceso de seguridad correctos para el acceso a la base de datos. En el menú Proyecto, seleccione Agregar carpeta de ASP.NET y, a continuación, App_Data.
Creación de una cadena de conexión en el archivo web.config
Agregaremos algunas líneas al archivo de configuración del sitio web para que Entity Framework sepa cómo conectarse a nuestra base de datos. Haga doble clic en el archivo Web.config ubicado en la raíz del proyecto.
Desplácese hasta la parte inferior de este archivo y agregue una sección <connectionStrings> directamente encima de la última línea, como se muestra a continuación.
<connectionStrings>
<add name="MusicStoreEntities"
connectionString="Data Source=|DataDirectory|MvcMusicStore.sdf"
providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>
</configuration>
Agdición de una clase de contexto
Haga clic con el botón derecho en la carpeta Models y agregue una nueva clase denominada MusicStoreEntities.cs.
Esta clase representará el contexto de base de datos de Entity Framework y controlará nuestras operaciones de creación, lectura, actualización y eliminación para nosotros. A continuación se muestra el código de esta clase.
using System.Data.Entity;
namespace MvcMusicStore.Models
{
public class MusicStoreEntities : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
}
}
Es decir, no hay ninguna otra configuración, interfaces especiales, etc. Al extender la clase base DbContext, nuestra clase MusicStoreEntities puede controlar nuestras operaciones de base de datos para nosotros. Ahora que tenemos ese enlace, vamos a agregar algunas propiedades más a nuestras clases de modelo para aprovechar parte de la información adicional de nuestra base de datos.
Adición de los datos del catálogo de tiendas
Aprovecharemos una característica de Entity Framework que agrega datos de "inicialización" a una base de datos recién creada. Esto rellenará previamente nuestro catálogo de tiendas con una lista de géneros, artistas y álbumes. La descarga MvcMusicStore-Assets.zip, que incluía nuestros archivos de diseño de sitio usados anteriormente en este tutorial, tiene un archivo de clase con estos datos de inicialización, ubicado en una carpeta denominada Code.
En la carpeta Code/Models, busque el archivo SampleData.cs y colóquelo en la carpeta Models del proyecto, como se muestra a continuación.
Ahora es necesario agregar una línea de código para indicar a Entity Framework sobre esa clase SampleData. Haga doble clic en el archivo Global.asax en la raíz del proyecto para abrirlo y agregar la siguiente línea a la parte superior del método Application_Start.
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(
new MvcMusicStore.Models.SampleData());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
En este momento, hemos completado el trabajo necesario para configurar Entity Framework para nuestro proyecto.
Consultar la base de datos
Ahora vamos a actualizar StoreController para que, en lugar de usar "datos ficticios", llame a nuestra base de datos para consultar toda su información. Comenzaremos declarando un campo en StoreController para contener una instancia de la clase MusicStoreEntities, denominada storeDB:
public class StoreController : Controller
{
MusicStoreEntities storeDB = new MusicStoreEntities();
Actualización del índice de almacén para consultar la base de datos
Entity Framework mantiene la clase MusicStoreEntities y expone una propiedad de colección para cada tabla de nuestra base de datos. Vamos a actualizar la acción Índice de StoreController para recuperar todos los géneros de nuestra base de datos. Anteriormente lo hicimos mediante datos de cadena de codificación rígida. Ahora, en su lugar, solo podemos usar la colección de géneros de contexto de Entity Framework:
public ActionResult Index()
{
var genres = storeDB.Genres.ToList();
return View(genres);
}
No es necesario que se produzcan cambios en nuestra plantilla de Vista, ya que todavía estamos devolviendo el mismo StoreIndexViewModel que hemos devuelto antes, solo vamos a devolver datos activos de nuestra base de datos ahora.
Cuando vuelva a ejecutar el proyecto y visite la dirección URL "/Store", ahora veremos una lista de todos los géneros de nuestra base de datos:
Actualización de la exploración y los detalles de la tienda para usar datos activos
Con el método de acción /Store/Browse?genre=[some-genre], buscamos un género por nombre. Solo esperamos un resultado, ya que nunca deberíamos tener dos entradas para el mismo nombre de género, por lo que podemos usar la extensión .Single() en LINQ para consultar el objeto Genre apropiado de esta manera (no escriba esto todavía):
var example = storeDB.Genres.Single(g => g.Name == "Disco");
El método Single toma una expresión Lambda como parámetro, que especifica que queremos un único objeto Genre de forma que su nombre coincida con el valor que hemos definido. En el caso anterior, estamos cargando un único objeto Genre con un valor Name que coincida con Disco.
Aprovecharemos una característica de Entity Framework que nos permite indicar otras entidades relacionadas que queremos cargar también cuando se recupere el objeto Genre. Esta característica se denomina Forma de resultados de consulta y nos permite reducir el número de veces que necesitamos acceder a la base de datos para recuperar toda la información que necesitamos. Queremos capturar previamente los álbumes para género que recuperamos, por lo que actualizaremos nuestra consulta para incluir de Genres.Include("Albums") e indicar que también queremos álbumes relacionados. Esto es más eficaz, ya que recuperará los datos de género y álbum en una sola solicitud de base de datos.
Habiendo explicado ya todo, este es el aspecto de nuestra acción actualizada de controlador Examinar:
public ActionResult Browse(string genre)
{
// Retrieve Genre and its Associated Albums from database
var genreModel = storeDB.Genres.Include("Albums")
.Single(g => g.Name == genre);
return View(genreModel);
}
Ahora podemos actualizar la vista Examinar de la Tienda para mostrar los álbumes que están disponibles en cada género. Abra la plantilla de vista (que se encuentra en /Views/Store/Browse.cshtml) y agregue una lista con viñetas de Álbumes, como se muestra a continuación.
@model MvcMusicStore.Models.Genre
@{
ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
<ul>
@foreach (var album in Model.Albums)
{
<li>
@album.Title
</li>
}
</ul>
Ejecutar nuestra aplicación y navegar a /Store/Browse?genre=Jazz muestra que nuestros resultados ahora se extraen de la base de datos, mostrando todos los álbumes en nuestro género seleccionado.
Realizaremos el mismo cambio en la dirección URL de /Store/Details/[id] y reemplazaremos nuestros datos ficticios por una consulta de base de datos que carga un álbum cuyo identificador coincide con el valor del parámetro.
public ActionResult Details(int id)
{
var album = storeDB.Albums.Find(id);
return View(album);
}
La ejecución de nuestra aplicación y la exploración a /Store/Details/1 muestra que nuestros resultados se extraen de la base de datos.
Ahora que la página Detalles de la Tienda está configurada para mostrar un álbum mediante el identificador del álbum, vamos a actualizar la vista Examinar para vincularla a la vista Detalles. Usaremos Html.ActionLink, exactamente como hicimos para vincular desde el Índice de la tienda a Examinar de la tienda al final de la sección anterior. El origen completo de la vista Examinar aparece a continuación.
@model MvcMusicStore.Models.Genre
@{
ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
<ul>
@foreach (var album in Model.Albums)
{
<li>
@Html.ActionLink(album.Title,
"Details", new { id = album.AlbumId })
</li>
}
</ul>
Ahora podemos navegar desde nuestra página Tienda a una página Género, que enumera los álbumes disponibles y haciendo clic en un álbum podemos ver los detalles de ese álbum.