MVC 5 Bootstraps Vista Razor
MVC 5 Bootstraps Vista Razor
MVC 5 Bootstraps Vista Razor
DISTRIBUIDAS
Razor minimiza el número de caracteres y tipeo necesario para escribir una plantilla de
vista y así permitir un trabajo rápido.
También puede hacer que la aplicación esté disponible a través de Internet mediante
el despliegue a un proveedor de hosting. Microsoft ofrece alojamiento web gratuito
para un máximo de 10 sitios web en una cuenta de prueba gratuita de Windows Azure
http://azure.microsoft.com/en-us/pricing/free-trial/?WT.mc_id=A443DD604
Modelos: Clases que representan los datos de la aplicación y que lógica de validación
usa para hacer cumplir las reglas de negocio para esos datos.
Vista: archivos de plantilla que la aplicación utiliza para generar dinámicamente
respuestas HTML.
Controlador: clases que manejan las solicitudes entrantes de navegación, recuperar
los datos del modelo, y luego especificar plantillas de vista que devuelven una
respuesta al navegador.
Primeros Pasos:
Comience instalando y ejecutando Visual Studio Express 2013 para Web del siguiente
enlace: http://www.visualstudio.com/downloads/download-visual-studio-vs#d-2013-
express
1
Creación de su primera aplicación
La siguiente imagen muestra cómo crear un Nuevo Proyecto. Comience seleccionando
“Visual C #” a la izquierda, a continuación, “Web” y seleccione Aplicación “Web
ASP.NET”. Ingrese el nombre de su proyecto, en este caso utilizaremos "MvcMovie".
Seleccione donde desea guardar su proyecto, por defecto Visual Studio lo guarda en
su carpeta Projects. Y luego haga clic en Aceptar.
2
En el nuevo proyecto de ASP.NET de diálogo, haga clic en la plantilla MVC y luego
haga clic en Aceptar.
3
Visual Studio utiliza una plantilla predeterminada para el proyecto ASP.NET MVC que
acaba de crear, por lo que tiene una aplicación de trabajo en este momento sin hacer
nada! Este es un simple "Hola Mundo!" proyecto, y es un buen lugar para comenzar su
aplicación.
4
A continuación podemos observar que en la parte superior derecho de la página
aparece el icono “IMAGEN”. Al hacer click en el mismo se desplazara el menú por
defecto de la plantilla el cual contiene campos como inicio, acerca de, registrar e inicio
de sesión
5
AGREGAR CONTROLADOR
A continuación se abrirá una ventana para elegir el tipo de controlador que deseamos
agregar, en este caso seleccionamos la opción Controlador de MVC 5: En
blanco y hacemos click en agregar.
.
6
Ahora nos pedirá que ingresemos el nombre del controlador, en este caso
ingresaremos HolaMundo como modo de ejemplo y presionamos agregar.
7
Abrimos el controlador que creamos y reemplazamos el código que contiene por el
siguiente:
8
Podemos observar la clase controlador HolaMundo que es la que creamos con
anterioridad, y dos método índice y Bienvenido, los cuales devuelven directamente
una cadena. Al Ejecutar la aplicación presionando F5 y agregando en la barra de
direcciones /HelloWorld observaremos el resultado del código ingresado.
Índice es el método por defecto que se llama en un controlador si no se especifica de
forma explícita.
9
/ [Controller] / [ActionName] / [Parameters]
10
Podemos observar que el parámetro numTime =1 toma el valor 1 por defecto en el
caso que no se le asigne un valor parámetro.
En la imagen anterior se puede ver que el tercer segmento URL se correspondía con
el parámetro de ruta ID. El método Welcome contiene un parámetro (ID) que coincide
con la URL especificada en el método RegisterRoutes.
En las aplicaciones ASP.NET MVC es típico pasar parámetros como datos de ruta
(como lo muestra la imagen anterior) que como cadena de consultas. También se
puede agregar una ruta para los parámetros name y numtimes en
App_Start\RouteConfig.cs.
Agregue el siguiente código en RouteConfig.cs:
11
Presione F5 y en la barra de direcciones ingrese la siguiente ruta:
localhost:xxx/HelloWorld/Welcome/Scott/3
Como pudimos observar hasta aquí aplicamos los conceptos de Vista – Controlador
(VC) del modelo MVC, es decir la visión y el trabajo del controlador.
Como se vio en sección anterior el método Index devolvía una cadena con un mensaje
que se encontraba en la clase controlador. Ahora vamos a cambiar dicho Index de tal
forma que devuelva un objeto View ingresando el siguiente código.
Este método utiliza una plantilla de vista para generar una respuesta HTML al
navegador. Tanto el método controlador (también conocidos como métodos de
acción ), como este Index , generalmente devuelven un ActionResult (o una
clase derivada de ActionResult ) y no tipos primitivos como cadena.
12
Haga clic derecho en la carpeta HelloWorld de views y haga clic en Agregar y,
a continuación, haga clic MVC 5 Ver Página con (Diseño Razor).
13
Podemos observar que el archivo Index.cshtml fue cread HelloWorld.
14
Agregue el siguiente Código:
15
Cambiar las vistas y el diseño de páginas
16
Para cambiar el contenido del título. Primero cambie el ActionLink en la plantilla
de diseño de "Application name" a "MVC Movie" y el controlador
de Inicio de Películas. El archivo de diseño completo se muestra a continuación:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
17
Examine el archivo Views\_ViewStart.cshtml, que contiene exactamente el
mismo beneficio que Razor. El archivo Views\_ViewStart.cshtml define el
diseño común que todas las vistas utilizarán, por lo tanto, puede comentar o
eliminar el código del archivo Views\HelloWorld\Index.cshtm.
@{
ViewBag.Title = "Movie List";
}
18
Pasar datos del Controlador a la Vista
19
public ActionResult Welcome ( string name , int numTimes =
1 )
{
ViewBag . Message = "Hello " + nombre ;
ViewBag . NumTimes = numTimes ;
20
pasa ese objeto a la vista. La vista a continuación, muestra al usuario los datos
como HTML.
Agregar Modelo
21
En el cuadro de dialogo ingrese el nombre “Movie”
22
Usaremos la clase Movie para representar películas en una base de
datos. Cada instancia de un objeto película corresponderá a una fila dentro de
una tabla de base de datos, y cada propiedad de la clase Movie se proyectará
en una columna en la tabla.
23
En esta sección vamos a añadir explícitamente una cadena de conexión en el
archivo Web.config de la aplicación.
LocalDB es una versión ligera del motor de base de datos SQL Server Express
que se inicia en la demanda y se ejecuta en modo de usuario. LocalDB ejecuta
en un modo especial de ejecución de SQL Server Express que le permite
trabajar con bases de datos como archivos .mdf. Normalmente, los archivos de
base de datos LocalDB se mantienen en la carpeta App_Data de un proyecto
web.
SQL Server Express no está recomendado para su uso en la producción de
aplicaciones web. LocalDB, en particular, no debe utilizarse para la producción
de una aplicación web, ya que no está diseñado para trabajar con IIS. Sin
embargo, una base de datos LocalDB se puede migrar cómodamente a SQL
Server o SQL Azure.
En Visual Studio 2013 (y en 2012), LocalDB se instala por defecto con Visual
Studio.
Por defecto, el Marco de la entidad busca una cadena de conexión con el
mismo nombre que la clase de contexto del objeto (MovieDBContext para este
proyecto).
24
Busque en el código del mismo el elemento <connectionStrings> :
El siguiente ejemplo muestra una parte del archivo Web.config con la nueva
cadena de conexión añadido:
<connectionStrings>
<add name="DefaultConnection" connectionString="Data
Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-
MvcMovie-20130603030321.mdf;Initial Catalog=aspnet-MvcMovie-
20130603030321;Integrated Security=True"
providerName="System.Data.SqlClient" />
<add name="MovieDBContext" connectionString="Data
Source=(LocalDB)\v11.0;AttachDbFilename=|DataDirectory|\Movies.mdf;Int
egrated Security=True" providerName="System.Data.SqlClient"
/>
25
La segunda cadena de conexión especifica una base de datos LocalDB
llamado Movie.mdf ubicado en la carpeta App_Data.
26
Para el campo “nombre del controlador” ingrese MoviesController.
Seleccione Movie (MvcMovie.Models) para la clase de modelo.
Seleccione MovieDBContext (MvcMovie.Models) para la clase de
contexto de datos.
27
Haga clic en Agregar. (Si se produce un error, es probable que no
construyeron la aplicación antes de comenzar la adición del controlador.) Visual
Studio crea los siguientes archivos y carpetas:
Un archivo MoviesController.cs en la carpeta Controllers.
Una carpeta Views\Movies. Con los siguientes archivos.
Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml y Index.cshtml.
Visual Studio crea automáticamente el CRUD (crear, leer, actualizar y
eliminar) los métodos de acción y puntos de vista (la creación automática de
métodos y puntos de vista de acción CRUD se conoce como scaffolding).
Ahora tiene una aplicación web completamente funcional que le permite crear,
listar, editar y eliminar entradas de cine.
Ejecute la aplicación y haga clic en el enlace MVC Movie (o busque
el controlador Movie añadiendo /Movies a la dirección URL en la barra de
direcciones de su navegador). Debido a que la aplicación se basa en la ruta por
defecto (definido en el archivo App_Start\RouteConfig.cs), el navegador http://
localhost:xxxxx/Movies se redirecciona al método Índex del controlador
Movies. En otras palabras, el navegador de
petición http://localhost:xxxxx/Movies es efectivamente el mismo que el
navegador petición http://localhost:xxxxx/Movies/Índex. El resultado es una lista
vacía de películas, porque no se ha añadido ninguna todavía.
28
Seleccione el enlace Crear nuevo. Ingrese algunos detalles sobre una película
y luego haga clic en el Crear botón.
29
// GET: /Movies/
public ActionResult Index()
{
return View(db.Movies.ToList());
}
30
película, la acción de los details e id a 1. También podrían pasar en el id con
una cadena de consulta de la siguiente manera:
http://localhost:xxxx/Movies/Details?id=1
Si se encuentra una película, se pasa una instancia del modelo de la
película a la vista Detalles:
return View(movie);
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>Movie</h4>
<hr />
<dl class="dl-horizontal">
<dt>
@Html.DisplayNameFor(model => model.Title)
</dt>
@*Markup omitted for clarity.*@
</dl>
</div>
<p>
@Html.ActionLink("Edit", "Edit", new { id = Model.ID }) |
@Html.ActionLink("Back to List", "Index")
</p>
31
Examine Index.cshtml de la plantilla Views y el método de Index en el
archivo MoviesController.cs. Observe cómo el código crea un objeto de
lista cuando se llama al metodo de ayuda Views en el método de la
acción Index.
public ActionResult Index()
{
return View(db.Movies.ToList());
}
Cuando creó el controlador de la película, Visual Studio incluye
automáticamente la siguiente declaración @modelo en la parte superior del
archivo Index.cshtml:
@model IEnumerable<MvcMovie.Models.Movie>
Esta directiva @Model le permite acceder a la lista de películas que el
controlador pasa a la vista mediante el uso de un objeto modelo que
está inflexible.
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<th>
@Html.DisplayFor(modelItem => item.Rating)
</th>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
@Html.ActionLink("Details", "Details", new { id=item.ID })
|
@Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
Dado que el objeto del modelo es de tipo fuerte (como un
IEnumerable <Movie> object), cada objeto de elemento en el circuito se escribe
como Movie.
32
Entre otras ventajas, esto significa que usted obtiene en tiempo de
compilación comprobación del código y la compatibilidad con
IntelliSense completo en el editor de código:
33
Haga doble clic en Movies.mdf para abrir el Explorador de servidores, expanda
la carpeta Tablas para ver la tabla de Películas.
34
Haga clic con el la tabla Movie y seleccione Mostrar tabla de datos para ver los
datos que ha creado.
35
Haga clic en la tabla Movies y seleccione Abrir tabla de definición para
ver la estructura de la tabla que el Código de Entity Framework Code First ha
creado para usted.
Observe cómo el esquema de la tabla Movies asigna a la clase movie que creó
anteriormente.
Entity Framework Code First crea automáticamente este esquema para usted
basado en su clase Movies.
Cuando haya terminado, cierre la conexión haciendo clic
derecho MovieDBContext y seleccionando Cerrar conexión. (Si usted no cierra
la conexión, es posible que obtenga un error la próxima vez que se ejecuta el
proyecto).
36
Ahora tiene una base de datos y páginas para visualizar, editar, actualizar y
eliminar datos.
using System;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
namespace MvcMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
37
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
}
38
El enlace Editar fue generada por el método Html.ActionLink en la vista
Views\Movies\Index.cshtml
@Html.ActionLink("Edit", "Edit", new { id=item.ID })
39
en App_Start\RouteConfig.cs) realiza el patrón de URL {controlador} / {acción} /
{id}.
Por lo tanto, ASP.NET traduce http://localhost:52947/Movies/Editar/3 en
una petición al método Edit acción del controlador Movies con el ID
de parámetro igual a 3. Examine el siguiente código del
archivo App_Start\RouteConfig.cs. El método MapRoute se utiliza para las
solicitudes HTTP ruta para el controlador y el método acción y suministrar el
parámetro opcional ID. El método MapRoute también es utilizado
por los HtmlHelper tales como ActionLink para generar URLs dado el
controlador, el método de acción y los datos de ruta.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index",
id = UrlParameter.Optional }
);
}
40
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
// POST: /Movies/Edit/5
// To protect from overposting attacks, please enable the specific
properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult
Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
@model MvcMovie.Models.Movie
@{
41
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.ID)
<div class="form-group">
@Html.LabelFor(model => model.Title, new { @class =
"control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
</div>
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
@Html.ValidationSummary(true)
42
@Html.HiddenFor(model => model.ID)
<div class="form-group">
@Html.LabelFor(model => model.Title, new { @class =
"control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.ReleaseDate, new { @class =
"control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
</div>
@*Genre and Price removed for brevity.*@
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-
default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
43
fuente de la página”. El código HTML para el elemento de formulario se muestra
a continuación.
<div class="control-group">
<label class="control-label" for="Title">Title</label>
<div class="controls">
<input class="text-box single-line" id="Title"
name="Title" type="text" value="GhostBusters" />
<span class="field-validation-valid help-inline" data-
valmsg-for="Title" data-valmsg-replace="true"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ReleaseDate">Release
Date</label>
<div class="controls">
<input class="text-box single-line" data-val="true" data-
val-date="The field Release Date must be a date." data-val-
required="The Release Date field is required." id="ReleaseDate"
name="ReleaseDate" type="date" value="1/1/1984" />
<span class="field-validation-valid help-inline" data-
valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="Genre">Genre</label>
<div class="controls">
<input class="text-box single-line" id="Genre"
name="Genre" type="text" value="Comedy" />
<span class="field-validation-valid help-inline" data-
valmsg-for="Genre" data-valmsg-replace="true"></span>
</div>
44
</div>
<div class="control-group">
<label class="control-label" for="Price">Price</label>
<div class="controls">
<input class="text-box single-line" data-val="true" data-
val-number="The field Price must be a number." data-val-required="The
Price field is required." id="Price" name="Price" type="text"
value="7.99" />
<span class="field-validation-valid help-inline" data-
valmsg-for="Price" data-valmsg-replace="true"></span>
</div>
</div>
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult
Edit([Bind(Include="ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
45
}
Tan pronto como la validación del lado del cliente determina que valores de un
campo no son válidos, se muestra un mensaje de error. Si
deshabilita JavaScript, usted no tendrá validación del lado del cliente, pero el
servidor detectará que los valores publicados no son válidos, y en los valores
del formulario se vuelven a mostrar los mensajes de error.
46
El método Create pasa un objeto de película vacío a la vista Crear.
Los métodos crear, editar, borrar o modificar los datos lo hacen en los métodos
de sobrecarga HttpPost. La Modificación de datos en un método HTTP GET
es un riesgo de seguridad, tal como se describe en la entrada
de blog MVCASP.NET Tip # 46 - No utilice enlace Delete porque crean agujeros
de seguridad. La Modificación de datos en un método GET HTTP también
viola las mejores prácticas y el patrón de arquitectura REST, que especifica
que las peticiones GET no deberían cambiar el estado de su solicitud. En otras
palabras, la realización de una operación GET debe ser una operación
segura que no tiene efectos secundarios y no modifica sus datos persistido.
47
El siguiente código muestra las modificaciones al archivo
Views\Movies\Edit.cshtml:
48
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
<script src="~/Scripts/globalize/globalize.js"></script>
<script
src="~/Scripts/globalize/cultures/globalize.culture.@(System.Threading
.Thread.CurrentThread.CurrentCulture.Name).js"></script>
<script>
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$(document).ready(function () {
Globalize.culture('@(System.Threading.Thread.CurrentThread.CurrentCult
ure.Name)');
});
</script>
<script>
jQuery.extend(jQuery.validator.methods, {
range: function (value, element, param) {
//Use the Globalization plugin to parse the value
var val = Globalize.parseFloat(value);
return this.optional(element) || (
val >= param[0] && val <= param[1]);
}
});
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
Globalize.parseDate(value) ||
Globalize.parseDate(value, "yyyy-MM-dd");
}
</script>
}
49
<system.web>
<globalization culture ="en-US" />
<!--elements removed for clarity-->
</system.web>
Search (Búsqueda)
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
50
Si el parámetro searchstring contiene una cadena, la consulta de peliculas se
modifica para filtrar en el valor de la cadena de búsqueda, usando el siguiente
código:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
Si cambiar la firma del método Index debe tener un parámetro llamado id, el
parámetro id coincidirá con el {id} marcador de posición para las rutas por
defecto establecidos en el archivo App_Start\RouteConfig.cs.
{ controller } / { acción } / { ID }
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
51
}
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
@model IEnumerable<MvcMovie.Models.Movie>
52
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
</p>
53
Con el archivo Index de View abierto en Visual Studio (como se muestra en la imagen
de arriba), toque Ctr F5 o F5 para ejecutar la aplicación y luego intentar la búsqueda
de una película.
No hay sobrecarga HttpPost del método Index. Usted no lo necesita, porque el
método no cambia el estado de la aplicación, el filtrado de datos.
Se podría añadir el siguiente método HttpPost Index. En ese caso, el invocador acción
podría coincidir con el método HttpPost Index, y el
método HttpPost Índice correría como se muestra en la imagen de abajo.
[HttpPost]
public string Index(FormCollection fc, string searchString)
{
return "<h3> From [HttpPost]Index: " + searchString + "</h3>";
}
Sin embargo, incluso si se agrega esta versión del metodo HttpPostIndex, hay
una limitación en la forma en que todo esto ha sido implementado. Imagine que
usted desea marcar una búsqueda particular o desea enviar un enlace a tus
amigos que pueden hacer clic con el fin de ver la misma lista filtrada de películas.
Observe que la dirección URL de la solicitud HTTP POST es la misma que la dirección
URL de la solicitud GET (localhost: xxxxx/Movies/Index) - no hay información de
búsqueda en la propia dirección URL. En este momento, la información de la
54
cadena de búsqueda se envía al servidor como un valor de campo de formulario.
Esto significa que no puede capturar esa información de búsqueda demarcador
o enviar a los amigos en una URL.
La solución es utilizar una sobrecarga de BeginForm que especifica que la petición
POST debe añadir la información de búsqueda de la URL y que debe ser enviado
a la HttpGet versión del método Index. Reemplace el método BeginForm sin
parámetros existente con el siguiente código:
@using (Html.BeginForm("Index","Movies",FormMethod.Get))
Ahora, cuando usted envía una búsqueda, la URL contiene una cadena de consulta
de búsqueda. La búsqueda también irá al método de acción HttpGet Index, incluso si
usted tiene un método HttpPost Index.
55
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (!string.IsNullOrEmpty(movieGenre))
{
movies = movies.Where(x => x.Genre == movieGenre);
}
return View(movies);
}
Esta versión del método Index toma un parámetro adicional nombrado movieGenre.
Las primeras líneas de código crean un objeto de lista para celebrar géneros
cinematográficos, desde la base de datos.
El código siguiente es una consulta LINQ que recupera todos los géneros, desde la
base de datos.
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
if (!string.IsNullOrEmpty(movieGenre))
{
movies = movies.Where(x => x.Genre == movieGenre);
56
}
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm("Index", "Movies", FormMethod.Get))
{
<p>
Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" />
</p>
}
</p>
<table class="table">
En el siguiente código:
@Html.DropDownList("movieGenre", "All")
57
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (!string.IsNullOrEmpty(movieGenre))
{
movies = movies.Where(x => x.Genre == movieGenre);
}
return View(movies);
}
58
En esta sección se ha creado un método de acción de búsqueda y la
vista que permiten a los usuarios buscar por título de la película y el género.
En esta sección vamos a usar Entity Framework Code First migraciones para
migrar algunos cambios en las clases del modelo de manera que se aplique el cambio
en la base de datos.
Por defecto, se utiliza Entity Framework Code First para crear automáticamente una
base de datos, como lo hizo anteriormente en este tutorial, Code First agrega una
tabla a la base de datos para ayudar a rastrear si el esquema de la base de datos está
en sintonía con el modelo de clases que fue generada. Si no están en sincronía, Entity
Framework genera un error. Esto hace que sea más fácil de localizar problemas en el
tiempo de desarrollo que de otro modo sólo podría encontrar (por errores oscuros) en
tiempo de ejecución.
59
.
60
El comando Enable-Migraciones (ver imagen superior) crea un
archivo Configuration.cs en una nueva carpeta de Migraciones.
61
new Movie
{
Title = "Ghostbusters ",
ReleaseDate = DateTime.Parse("1984-3-13"),
Genre = "Comedy",
Price = 8.99M
},
new Movie
{
Title = "Ghostbusters 2",
ReleaseDate = DateTime.Parse("1986-2-23"),
Genre = "Comedy",
Price = 9.99M
},
new Movie
{
Title = "Rio Bravo",
ReleaseDate = DateTime.Parse("1959-4-15"),
Genre = "Western",
Price = 3.99M
}
);
Haga clic derecho sobre la línea roja ondulada bajo Movie y selectResolve y
luego haga clic en usingMvcMovie.Models;
62
Si lo hace, añade la siguiente instrucción using:
using MvcMovie.Models;
Code First Migration crea otro archivo de clase en la carpeta de Migraciones (con el
nombre {DateStamp} _Initial.cs), y esta clase contiene código que crea el esquema
de base de datos. El nombre del archivo de migración está pre-fijada con una marca
de tiempo para ayudar con el pedido. Examine el archivo {DateStamp} _Initial.cs, que
contiene las instrucciones para crear la tabla Películas para la película DB. Al
actualizar la base de datos en las instrucciones a continuación, este
archivo {DateStamp} _Initial.cs se ejecutará y creara el esquema de base de datos.
A continuación, el método de Seed correrá a poblar la DB con los datos de prueba.
En la consola del Administrador de paquetes, tipee el comando update-database para crear la
base de datos y ejecutar el método Seed
63
Si recibe un error que indica una tabla ya existe y no se puede crear, es
probablemente porque se le terminó la aplicación después de que ha eliminado la
base de datos y antes de ejecutar update-database. En ese caso, elimine el
archivo Movies.mdf de nuevo y vuelva a intentar el comando update-database. Si
continúa recibiendo un error, elimine la carpeta migraciones y contenido a
continuación, iniciar con las instrucciones en la parte superior de esta
página (es borrar el archivo Movies.mdf luego proceder a Enable-Migraciones).
Ejecute la aplicación y vaya a la URL /Movies. Se muestran los datos del Seed.
Comience por agregar una nueva propiedad de Calificación a la clase Movie existente.
Abra el archivo models\Movie.cs y añada la propiedad Rating como éste:
public string Rating { get; set; }
La clase completa de Movie ahora se ve como el siguiente código:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
64
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
public decimal Price { get; set; }
public string Rating { get; set; }
}
También es necesario actualizar las plantillas de vista con el fin de visualizar, crear y
editar la nueva propiedad Rating en la vista del navegador.
Abra el archivo \Viewa\Movies\Index.cshtml y añada un <th>Rating</ th> encabezado
de columna justo después de la columna Precio. A continuación, agregue un elemento
columna <td> cerca del final de la plantilla para tener el valor@item.Rating. A
continuación se muestra la plantilla de vista Index.cshtml actualizada:
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm("Index", "Movies", FormMethod.Get))
{
<p>
Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" />
</p>
}
</p>
<table class="table">
<tr>
<th>
@Html.DisplayNameFor(model => model.Title)
</th>
<th>
@Html.DisplayNameFor(model => model.ReleaseDate)
65
</th>
<th>
@Html.DisplayNameFor(model => model.Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.Price)
</th>
<th>
@Html.DisplayNameFor(model => model.Rating)
</th>
<th></th>
</tr>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
@Html.ActionLink("Details", "Details", new { id=item.ID })
|
@Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
66
A continuación, abra la carpeta \Views\Movies\Create.cshtml y agregue el
campo Clasificación con el siguiente marcado highlighed. Esto hace que un cuadro de
texto pueda especificar una clasificación cuando se crea una nueva película.
<div class="form-group">
@Html.LabelFor(model => model.Price, new { @class = "control-
label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Rating, new { @class = "control-
label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Rating)
@Html.ValidationMessageFor(model => model.Rating)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default"
/>
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
67
El modelo de respaldo al contexto 'MovieDBContext' ha cambiado desde que se
creó la base de datos. Considere el uso de Code First Migrations de actualizar la base
de datos (http://go.microsoft.com/fwlink/?LinkId=238269).
68
Estás viendo este error porque la clase del modelo Movie actualizadoen la
aplicación es ahora diferente que el esquema de la tabla de la película de la base de
datos existente. (No hay columna de Clasificación en la tabla de base de datos.).
Cuando este comando finalice, Visual Studio abre el archivo de clase que define la
nueva clase DbMIgration derivados, y en el método de arriba se puede ver el código
que crea la nueva columna.
public partial class AddRatingMig : DbMigration
{
public override void Up()
{
AddColumn("dbo.Movies", "Rating", c => c.String());
}
69
Vuelva a ejecutar la aplicación y vaya a la URL /Movies
Haga clic en el enlace crear nuevo para agregar una nueva película. Tenga en cuenta
que usted puede añadir una calificación.
70
Haga click en Crear. La nueva película, incluida la clasificación, ahora aparece en las
películas del anuncio:
Ahora que el proyecto es el uso de las migraciones, usted no tendrá que dejar la base
de datos cuando se agrega un nuevo campo o actualizar de otra manera el esquema.
También debe agregar el campo Rating a la edición, Detalles Borrar plantillas de vista.
Puede introducir el comando "update-database" en la ventana de la consola del
Administrador de paquetes nuevo y sin código demigración tendría que
ejecutar, porque el esquema coincide con el modelo. Sin embargo, ejecutar "update-
database" se ejecutará el método de Seed de nuevo, y si ha cambiado alguno de los
datos del Seed, los cambios se perderán porque los datos del método de
Seed upserts.
En esta sección usted vio cómo se pueden modificar los objetos del
modelo y mantener la base de datos en sincronía con los cambios. También ha
aprendido una manera de rellenar una base de datos recién creada con datos de la
muestra para que pueda probar escenarios.
71
Adición de Validación
Echemos un vistazo a cómo usted puede tomar ventaja de este soporte de validación en la
aplicación de la película.
72
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime ReleaseDate { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
[Required]
[StringLength(30)]
public string Genre { get; set; }
[Range(1, 100)]
[DataType(DataType.Currency)]
public decimal Price { get; set; }
[RegularExpression(@"^[A-Z]+[a-zA-Z''-'\s]*$")]
[StringLength(5)]
public string Rating { get; set; }
}
73
:
Cuando estos comandos terminen, Visual Studio abre el archivo de clase que define la
nueva DbMIgration clase derivada con el nombre especificado (DataAnnotations), y en
el método de arriba se puede ver el código que actualiza las restricciones de
esquema:
public override void Up()
{
AlterColumn("dbo.Movies", "Title", c => c.String(maxLength: 60));
AlterColumn("dbo.Movies", "Genre", c => c.String(nullable: false,
maxLength: 30));
AlterColumn("dbo.Movies", "Rating", c => c.String(maxLength: 5));
}
74
La longitud mínima de 3 en el Título y el rango de precio no creó los cambios de
esquema
Los campos de cadena muestran los nuevos límites de longitud y Género ya no está
marcada como anulable.
Los atributos de validación especifican el comportamiento que desea aplicar a las del
modelo que se aplican. Los atributos Required y MinimumLength indican que
una propiedad debe tener un valor; pero nada impide que un usuario entrar en el
espacio blanco para satisfacer esta validación. El atributo RegularExpression se utiliza
para limitar lo que los personajes se pueden introducir. En el código anterior, Género y
clasificación deben utilizar sólo letras (espacios en blanco, números y caracteres
especiales no están permitidos). El atributo Range limita a un valor dentro de un rango
especificado. El atributo StringLength le permite ajustar la longitud máxima de una
propiedad de cadena y, opcionalmente, su longitud mínima. Los tipos de
valor (como decimal, int, float, DateTime) son inherentemente necesarios y no
necesitan el atributo Required.
Code First asegura que las reglas de validación que especifique en una clase del
modelo se aplican antes de que la aplicación guarde los cambios en la base de datos.
Por ejemplo, el código de
abajo arrojará una excepciónDbEntityValidationException cuando se llama al método
SaveChanges, debido a que varios valores de propiedad de película requerida faltan:
75
Error de validación de una o más entidades.Ver propiedades. 'EntityValidationErrors' para mas
detalles.
Haga clic en el enlace crear nuevo para agregar una nueva película. Rellene
el formulario con algunos valores no válidos. Tan pronto como la validación del lado
del cliente jQuery detecta el error, se muestra un mensaje de error.
Un beneficio real es que usted no necesita cambiar una sola línea de código de la
clase MoviesController o en la vista Create.cshtml a fin de que esta interfaz de usuario de
76
validación. El controlador y puntos de vista que creó anteriormente en este
tutorial recogidos automáticamente las reglas de validación que ha
especificado mediante el uso de la validación de atributos en las propiedades de la
clase del modelo de la película. Validación de la prueba utilizando el método de acción
Edit, y se aplica la misma validación.
Los datos del formulario no se envían al servidor hasta que no haya errores de
validación del lado del cliente. Usted puede verificar esto poniendo un punto de
quiebre en el método HTTP POST, mediante el uso de la herramienta de fiddler,o las
herramientas de desarrollo F12 IE.
77
validación, el método vuelve a mostrar el formulario Create. Si no hay errores, el
método guarda la nueva película en la base de datos. En nuestro ejemplo de la
película, el formulario no se ha publicado en el servidor cuando hay errores de
validación detectados en el lado del cliente; el segundo método Create nunca se
llama. Si desactiva JavaScript en su navegador, la validación del cliente está
desactivada y el método HTTP POST Create llama ModelState.IsValid para
comprobar si la película tiene algún error de validación.
78
La siguiente imagen muestra cómo desactivar JavaScript en el navegador FireFox.
79
A continuación se muestra la plantilla de vista Create.cshtml de
scaffolded anteriormente. Es usado por los métodos de acción que se muestran
arriba tanto para mostrar la forma inicial y para volver a mostrar en caso de un error.
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Movie</h4>
<hr />
@Html.ValidationSummary(true)
80
<div class="form-group">
@Html.LabelFor(model => model.Title, new { @class =
"control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
</div>
@*Fields removed for brevity.*@
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-
default" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
81
mantiene el código muy limpio, y hace que sea fácil de mantener y evolucionar. Y
significa que que podrás honrar plenamente el principio DRY.
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
[DataType(DataType.Currency)]
public decimal Price { get; set; }
El atributo DataType sólo proporcionan consejos para el motor a fin de dar formato a
los datos (y los atributos de suministro como <a> para URL y <a
href="mailto:EmailAddress.com"> por correo electrónico. Puede utilizar el atributo
RegularExpression para validar el formato de los datos. El atributo DataType se utiliza
para especificar el tipo de datos que es más específico que el tipo intrínseco base de
datos, que no atribuye validación. En este caso, sólo queremos hacer un seguimiento
de la fecha, no la fecha y la hora. La enumeración DataType ofrece para muchos tipos
de datos, como fecha, hora, Fax, vigencia EmailAddress y más. El atributo DataType
también puede activar la aplicación para proporcionar automáticamente las
características específicas del tipo, por ejemplo, a. mailto: enlace se pueden crear
para DataType.EmailAddress, y un selector de fecha se pueden proporcionar para
DataType.Date en los navegadores que soportan HTML5. El atributo DataType emite
datos HTML 5 (que se pronuncia data dash) atribuye que los navegadores HTML 5
puedan entender. Los atributos DataType no proporcionan ninguna validación.
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}",
ApplyFormatInEditMode = true)]
public DateTime EnrollmentDate { get; set; }
82
mismo, pero es generalmente una buena idea utilizar el atributo DataType también. El
atributo DataType transmite la semántica de los datos en comparación con el modo de
hacer en una pantalla, y proporciona los siguientes beneficios que no se consigue
con formatSalida:
El navegador puede habilitar las características de HTML5 (por ejemplo, para
mostrar un control de calendario, el símbolo de moneda de la configuración
regional apropiada, enlaces de correo electrónico,etc.).
Por defecto, el navegador representar datos con el formato correcto basado
en la configuración regional.
El atributo DataType puede permitir MVC para elegir la plantilla de
campo derecho de representar los datos (la formatSalida si se usa
solo utiliza la plantilla de cadena).
El código siguiente muestra los atributos que combinan en una sola línea:
En esta parte del tutorial, examinar los métodos Details generados automáticamente y
Delete.
83
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
Code First hace que sea fácil de buscar datos utilizando el método Find. Una
característica importante de seguridad incorporada en el método es que el
código verifica que el método Find ha encontrado una película antes de que el código
intentara hacer algo con ella. Por ejemplo, un hacker podría introducir errores en el
sitio cambiando la URL creado por los enlaces
desde http: //localhost:xxxx/Movies/Details/1 a algo
como http: //localhost:xxxx/Movies/Details/12345 (o algún otro valor que no
represente una película real). Si no marcó para una película nulo, una
película nula daría lugar a un error de base de datos.
// GET: /Movies/Delete/5
84
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
// POST: /Movies/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Movie movie = db.Movies.Find(id);
db.Movies.Remove(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
El método HttpPost que borra los datos se denomina DeleteConfirmed para dar el
método HTTP POST una firma o nombre único. Las dos firmas de los métodos se
muestran a continuación:
// GET: /Movies/Delete/5
public ActionResult Delete(int? id)
//
// POST: /Movies/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
85
El Common Language Runtime (CLR) requiere métodos sobrecargados para
tener una firma única de parámetros (mismo nombre de método, pero diferente lista
de parámetros). Sin embargo, aquí se necesitan dos métodos Eliminar - uno para
GET y otro para la POST - que ambos tienen la misma firma parámetro.
(Ambos tienen que aceptar un solo entero como parámetro.)
Para solucionar esto, se puede hacer un par de cosas. Una es la de dar a
los métodos diferentes nombres. Eso es lo que el mecanismo de Scaffolding hizo en el
ejemplo anterior. Sin embargo, esto presenta un pequeño problema: en ASP.NET los
segmentos de una URL a los mapean métodos de acción por su nombre, y si cambia
el nombre de un método, el enrutamiento normalmente no sería capaz de encontrar
ese método. La solución es lo que se ve en el ejemplo, que consiste en añadir el
atributo ActionName ("Delete") para el método DeleteConfirmed. Esto
realiza efectivamente mapeo para el sistema de enrutamiento para que una URL que
incluya /Delete/ para una solicitud POST se encuentra el método DeleteConfirmed.
Otra forma común para evitar un problema con métodos que tienen nombres y
firmas idénticas es cambiar artificialmente la firma del método POST para incluir un
parámetro no utilizado. Por ejemplo, algunos desarrolladores agregar un tipo de
parametro FormCollection que se pasa al método POST, y luego simplemente no
usan el parámetro:
public ActionResult Delete(FormCollection fcNotUsed, int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
db.Movies.Remove(movie);
db.SaveChanges();
return RedirectToAction("Index");
}
Resumen
Ahora tiene una aplicación completa ASP.NET MVC que almacena los datos en
una base de datos local DB. Puede crear, leer, actualizar, eliminar y buscar películas.
86
87