Agregando un campo nuevo a una aplicación ASP.NET Core MVC
En esta sección vamos a usar migraciones Code First de Entity Framework para agregar un campo nuevo al modelo y migrar ese cambio a la base de datos.
Cuando usamos EF Code First para crear de manera automática la base de datos, Code First agrega una tabla en la base de datos para llevar cuenta de si el esquema de base de datos está sincronizado con el modelo de clases del que fue generado. Si no estan sincronizados, EF lanza una excepción. Esto hace que sea más facil encontrar problemas de insconsistencia entre la base de datos y el código.
Agregar la propiedad Rating al modelo de películas
Abramos el archivo Models/Movie.cs y agreguemos una propiedad Rating
:
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
public string Rating { get; set; }
}
Compilemos la aplicación (Ctrl+Shift+B)
Porque agregamos un campo nuevo a la clase Movie
, tambien necesitamos actualizar
la lista blanca de campos permitidos para incluir esta nueva propiedad. En
el archivo MovieController.cs, actualiza el atributo [Bind]
para los métodos
Create
y Edit
para que incluyan la propiedad:
[Bind("ID,Title,ReleaseDate,Genre,Price,Rating")]
También vamos a necesitar actualizar las plantillas de vista para que muestren,
creen y editen la nueva propiedad Rating
.
Editemos el archivo /Views/Movies/Index.cshtml y agreguemos un campo Rating
:
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.movies[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.movies[0].ReleaseDate)
</th>
<th>
@Html.DisplayNameFor(model => model.movies[0].Genre)
</th>
<th>
@Html.DisplayNameFor(model => model.movies[0].Price)
</th>
<th>
@Html.DisplayNameFor(model => model.movies[0].Rating)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.movies)
{
<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>
<td>
@Html.DisplayFor(modelItem => item.Rating)
</td>
<td>
Actualicemos el archivo /Views/Movies/Create.cshtml agregando un campo Rating
.
Podemos copiar y pegar el “form group” anterior y permitir que IntelliSense nos ayude
a actualizar los campos. IntelliSense funciona con los Tag Helpers
Nota: Si tenemos una versión desactualizada de Visual Studio 2017 puede que necesitemos
instalar Razor Language Services para Razor IntelliSense.
La aplicación no va a funcionar hasta que no actualicemos la base de datos para que incluya
el campo nuevo. Si lo ejecutamos ahora, vamos a obtener la siguiente excepción
SqlException
:
SqlException: Invalid column name 'Rating'.
Estamos viendo este error porque el modelo de la clase Movie actualizada es diferente del esquema de la tabla Movie en la base de datos existente. (No hay columna Rating en la tabla de la base de datos).
Hay algunas formas de resolver el error:
- Hagamos que Entity Framework borre y después vuelva a crear la base de datos basándose en un nuevo modelo de esquema de clases. Esta manera de trabajar es muy conveniente en etapas tempranas del ciclo de desarrollo cuando no sabemos bien la forma que va a tener nuestro modelo / base de datos. Nos permite ir cambiando de manera rápida el modelo y el esquema de base de datos al mismo tiempo. La desventaja, sin embargo, es que vamos a perder los datos existentes en la base de datos. Así que no vamos a hacer esto en una base de datos de producción. Utilizar un inicializador para hacer de semilla a una base de datos con datos de producción es una manera muy efectiva de trabajar.
- Modificar el esquema de la base de datos existente de manera explícita para que coincida con las clases del modelo. La ventaja de esta manera de trabajar es que conservaremos los datos existentes. Se pueden hacer los cambios de manera manual o creando un script de cambios para la base de datos.
- Utilizar migraciones Code First para actualizar el esquema de base de datos.
Actualicemos la clase SeedData
para que provea un valor para la nueva columna. Un ejemplo
del cambio se muestra a continuación, pero debemos asegurarnos de hacer este cambio
para cada uno de los new Movie
.
new Movie
{
Title = "When Harry Met Sally",
ReleaseDate = DateTime.Parse("1989-1-11"),
Genre = "Romantic Comedy",
Rating = "R",
Price = 7.99M
},
Compilemos la solución.
Desde el menú Herramientas, seleccionemos Administrador de paquetes Nuget > Consola del Administrador de Paquetes.
En la consola, ingresa los siguientes comandos:
Add-Migration Rating
Update-Database
El comando Add-Migration
le dice al framework de migraciones que examine el modelo
actual de la clase Movie
con el esquema actual de la base de datos y cree el código
necesario para migrar la base de datos al modelo nuevo. El nombre “Rating” es arbitrario
y se usa para nombrar el archivo de migración. Una cosa muy util acá es poner un nombre
que sea representativo.
Si borramos todos los registros en la base de datos, la inicialización de la clase SeedData
va a generar datos nuevos e incluir el campo Rating
. El borrado se puede hacer desde los
enlaces Delete en la aplicación o usando el SQL Server Object Explorer.
Ejecutemos la palicación y verifiquemos que podemos crear/editar/mostrar películas
con el campo Rating
. También deberíamos ver que el campo Rating
esté agregado a las
vistas Edit
, Details
y Delete
.
En la siguiente parte del tutorial vamos a ver la validación en detalle.