Ejercicio: Configuración de una migración
En esta unidad, se crean clases de entidad de C# que se asignarán a las tablas de una base de datos SQLite local. La característica de migraciones de EF Core genera tablas a partir de esas entidades.
Una migración permite actualizar incrementalmente el esquema de la base de datos.
Obtención de los archivos del proyecto
Para empezar, obtenga los archivos del proyecto. Tiene algunas opciones para obtener los archivos del proyecto:
- Uso de GitHub Codespaces
- Clonación del repositorio de GitHub
Si tiene instalado un entorno de ejecución de contenedor compatible, también puede usar la extensión Dev Containers para abrir el repositorio en un contenedor con las herramientas preinstaladas.
Uso de GitHub Codespaces
Un codespace es un IDE hospedado en la nube. Si usa GitHub Codespaces, vaya al repositorio en el explorador. Seleccione Código y, a continuación, cree un codespace en la rama main
.
Clonación del repositorio de GitHub
Si no usa GitHub Codespaces, puede clonar el repositorio de GitHub del proyecto y, después, abrir los archivos como si fueran una carpeta en Visual Studio Code.
Abra un terminal de comandos y clone el proyecto desde GitHub mediante el símbolo del sistema:
git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
Vaya a la carpeta mslearn-persist-data-ef-core y abra el proyecto en Visual Studio Code:
cd mslearn-persist-data-ef-core code .
Revisión del código
Ahora que tiene los archivos de proyecto, veamos lo que hay en el proyecto y examinemos el código.
- El proyecto, una API web de ASP.NET Core, se encuentra en el directorio ContosoPizza. Las rutas de acceso de archivo a las que hacemos referencia en este módulo están relacionadas con el directorio ContosoPizza.
- Services/PizzaService.cs es una clase de servicio que define los métodos de creación, lectura, actualización y eliminación (CRUD). Actualmente, todos los métodos inician
System.NotImplementedException
. - En Program.cs,
PizzaService
se registra en el sistema de inserción de dependencias de ASP.NET Core. - Controllers/PizzaController.cs es un valor de
ApiController
que expone un punto de conexión para los verbos HTTP POST, GET, PUT y DELETE. Estos verbos llaman a los métodos CRUD correspondientes enPizzaService
.PizzaService
se inserta en el constructorPizzaController
. - La carpeta Models contiene los modelos que
PizzaService
yPizzaController
usan. - Los modelos de entidad, Pizza.cs, Topping.csy Sauce.cs, tienen las relaciones siguientes:
- Una pizza puede tener uno o varios ingredientes.
- Se puede usar un ingrediente en una o muchas pizzas.
- Una pizza puede llevar una salsa, pero una salsa se puede utilizar en muchas pizzas.
Compilar la aplicación
Para compilar la aplicación en Visual Studio Code:
En el panel del Explorador, haga clic con el botón derecho en el directorio ContosoPizza y seleccione Abrir en terminal integrado.
Se abre un panel de terminal con ámbito en el directorio ContosoPizza.
Compile la aplicación mediante el comando siguiente:
dotnet build
El código debe compilarse sin advertencias ni errores.
Adición de paquetes NuGet y herramientas de EF Core
El motor de base de datos con el que trabajará en este módulo es SQLite. SQLite es un motor de base de datos ligero basado en archivos. Es una buena opción para el desarrollo y las pruebas, y también es una buena opción para las implementaciones de producción a pequeña escala.
Nota
Como se mencionó anteriormente, los proveedores de bases de datos de EF Core se pueden conectar. SQLite es una buena opción para este módulo porque es ligero y multiplataforma. Puede usar el mismo código para trabajar con diferentes motores de base de datos, como SQL Server y PostgreSQL. Incluso puede usar varios motores de base de datos en la misma aplicación.
Antes de empezar, agregue los paquetes necesarios:
En el panel del terminal, ejecute el siguiente comando:
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Este comando agrega el paquete NuGet que contiene el proveedor de bases de datos SQLite de EF Core y todas sus dependencias, incluidos los servicios de EF Core comunes.
Luego, ejecute este comando:
dotnet add package Microsoft.EntityFrameworkCore.Design
Este comando agrega los paquetes necesarios para las herramientas de EF Core.
Para finalizar, ejecute este comando:
dotnet tool install --global dotnet-ef
Este comando instala
dotnet ef
, la herramienta que usará para crear migraciones y scaffolding.Sugerencia
Si
dotnet ef
ya está instalado, puede actualizarlo mediante la ejecución dedotnet tool update --global dotnet-ef
.
Scaffolding de modelos y DbContext
Ahora agregará y configurará una implementación de DbContext
. DbContext
es una puerta de enlace a través de la cual puede interactuar con la base de datos.
Haga clic con el botón derecho en el directorio ContosoPizza y agregue una nueva carpeta denominada Data.
En la carpeta Data, cree un archivo llamado PizzaContext.cs. Agregue el código siguiente al archivo vacío:
using Microsoft.EntityFrameworkCore; using ContosoPizza.Models; namespace ContosoPizza.Data; public class PizzaContext : DbContext { public PizzaContext (DbContextOptions<PizzaContext> options) : base(options) { } public DbSet<Pizza> Pizzas => Set<Pizza>(); public DbSet<Topping> Toppings => Set<Topping>(); public DbSet<Sauce> Sauces => Set<Sauce>(); }
En el código anterior:
- El constructor acepta un parámetro de tipo
DbContextOptions<PizzaContext>
. El constructor permite que el código externo pase la configuración, para que se pueda compartir el mismoDbContext
entre el código de prueba y de producción e incluso usarse con distintos proveedores. - Las propiedades
DbSet<T>
se corresponden con las tablas que se crean en la base de datos. - Los nombres de tabla coincidirán con los nombres de propiedad
DbSet<T>
de la clasePizzaContext
. Puede invalidar este comportamiento si es necesario. - Cuando se crea una instancia,
PizzaContext
expondrá las propiedadesPizzas
,Toppings
ySauces
. Los cambios que se realicen en las colecciones que esas propiedades exponen se propagarán a la base de datos.
- El constructor acepta un parámetro de tipo
En Program.cs, reemplace
// Add the PizzaContext
por el código siguiente:builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
El código anterior:
- Registra
PizzaContext
en el sistema de inserción de dependencias de ASP.NET Core. - Especifica que
PizzaContext
usará el proveedor de bases de datos SQLite. - Define una cadena de conexión SQLite que apunta a un archivo local, ContosoPizza.db.
Nota:
SQLite usa archivos de bases de datos locales, por lo que es correcto codificar de forma rígida la cadena de conexión. Para bases de datos de red como PostgreSQL y SQL Server, siempre debe almacenar las cadenas de conexión de forma segura. Para el desarrollo local, use el administrador de secretos. En el caso de las implementaciones de producción, considere la posibilidad de usar un servicio como Azure Key Vault.
- Registra
En Program.cs, reemplace
// Additional using declarations
por el código siguiente.using ContosoPizza.Data;
Este código resuelve las dependencias del paso anterior.
Guarde todos los cambios. Github Codespaces guarda automáticamente los cambios.
Compile la aplicación en el terminal mediante la ejecución de
dotnet build
. La compilación debe realizarse correctamente sin advertencias ni errores.
Creación y ejecución de una migración
Después, cree una migración que puede usar para crear la base de datos inicial.
En el terminal cuyo ámbito es la carpeta del proyecto ContosoPizza, ejecute el siguiente comando para generar una migración para crear las tablas de la base de datos:
dotnet ef migrations add InitialCreate --context PizzaContext
En el comando anterior:
- La migración se denomina: InitialCreate.
- La opción
--context
especifica el nombre de la clase en el proyecto ContosoPizza, que se deriva deDbContext
.
Aparece un nuevo directorio Migrations en la raíz del proyecto ContosoPizza. El directorio contiene un archivo <timestamp>_InitialCreate.cs en el que se describen los cambios de la base de datos que se van a traducir en un script de cambios de lenguaje de definición de datos (DDL).
Ejecute el comando siguiente para aplicar la migración InitialCreate:
dotnet ef database update --context PizzaContext
Este comando aplica la migración. ContosoPizza.db no existe, por lo que este comando crea la migración en el directorio del proyecto.
Sugerencia
Todas las plataformas admiten la herramienta
dotnet ef
. En Visual Studio en Windows, también puede usar los cmdletsAdd-Migration
yUpdate-Database
de PowerShell en la ventana Consola del Administrador de paquetes integrada.
Inspección de la base de datos
EF Core creó una base de datos para la aplicación. Luego, echemos un vistazo a la base de datos mediante la extensión SQLite.
En el panel Explorador, haga clic con el botón derecho en el archivo ContosoPizza.db y seleccione Abrir base de datos.
Aparece la carpeta SQLite Explorer en el panel Explorador.
Seleccione la carpeta Explorador de SQLite para expandir el nodo y todos sus nodos secundarios. Haga clic con el botón derecho en ContosoPizza.db y seleccione Mostrar tabla "sqlite_master" para ver el esquema de la base de datos completo y las restricciones que creó la migración.
- Se crearon tablas que corresponden a cada entidad.
- Los nombres de tabla se tomaron de los nombres de las propiedades
DbSet
enPizzaContext
. - Las propiedades denominadas
Id
se infirieron como campos de clave principal de incremento automático. - Las convenciones de nomenclatura de la clave principal y la clave externa de EF Core son
PK_<primary key property>
yFK_<dependent entity>_<principal entity>_<foreign key property>
, respectivamente. Los marcadores de posición<dependent entity>
y<principal entity>
se corresponden con los nombres de clase de la entidad.
Nota:
Al igual que ASP.NET Core MVC, EF Core usa un enfoque de convención sobre configuración. Las convenciones de EF Core acortan el tiempo de desarrollo al deducir la intención del desarrollador. Por ejemplo, el núcleo de EF infiere una propiedad denominada
Id
o<entity name>Id
para ser la clave principal de la tabla generada. Si decide no usar la convención de nomenclatura, debe anotar la propiedad con el atributo[Key]
o configurarla como clave en el métodoOnModelCreating
deDbContext
.
Cambio del modelo y actualización del esquema de base de datos
El administrador de Contoso Pizza le proporciona algunos requisitos nuevos, por lo que debe cambiar los modelos de entidad. En los pasos siguientes, va a modificar los modelos mediante atributos de asignación (a veces denominados anotaciones de datos).
En Models\Pizza.cs, realice los cambios siguientes:
- Agregue una directiva
using
paraSystem.ComponentModel.DataAnnotations
. - Agregue un atributo
[Required]
antes de la propiedadName
para marcar la propiedad según sea necesario. - Agregue un atributo
[MaxLength(100)]
antes de la propiedadName
para especificar una longitud de cadena máxima de 100.
El archivo Pizza.cs actualizado debe ser similar al siguiente código:
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Pizza { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public Sauce? Sauce { get; set; } public ICollection<Topping>? Toppings { get; set; } }
- Agregue una directiva
En Models\Sauce.cs, realice los cambios siguientes:
- Agregue una directiva
using
paraSystem.ComponentModel.DataAnnotations
. - Agregue un atributo
[Required]
antes de la propiedadName
para marcar la propiedad según sea necesario. - Agregue un atributo
[MaxLength(100)]
antes de la propiedadName
para especificar una longitud de cadena máxima de 100. - Agregue una propiedad
bool
llamadaIsVegan
.
El archivo Sauce.cs actualizado debe ser similar al siguiente código:
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Sauce { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public bool IsVegan { get; set; } }
- Agregue una directiva
En Models\Topping.cs, realice los cambios siguientes:
Agregue directivas
using
paraSystem.ComponentModel.DataAnnotations
ySystem.Text.Json.Serialization
.Agregue un atributo
[Required]
antes de la propiedadName
para marcar la propiedad según sea necesario.Agregue un atributo
[MaxLength(100)]
antes de la propiedadName
para especificar una longitud de cadena máxima de 100.Agregue una propiedad
decimal
llamadaCalories
inmediatamente después de la propiedadName
.Agregue una propiedad
Pizzas
de tipoICollection<Pizza>?
. Este cambio hace quePizza
-Topping
sea una relación de varios a varios.Agregue el atributo
[JsonIgnore]
a la propiedadPizzas
.Importante
Este atributo sirve para evitar que las entidades
Topping
incluyan la propiedadPizzas
cuando el código de la API web serializa la respuesta a JSON. Sin este cambio, una colección serializada de ingredientes incluiría una colección de cada pizza que usa el ingrediente. Cada pizza de esa colección contendría una colección de ingredientes, y cada uno de ellos, a su vez, contendría una colección de pizzas. Este tipo de bucle infinito se denomina referencia circular y no se puede serializar.
El archivo Topping.cs actualizado debe ser similar al siguiente código:
using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace ContosoPizza.Models; public class Topping { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public decimal Calories { get; set; } [JsonIgnore] public ICollection<Pizza>? Pizzas { get; set; } }
Guarde todos los cambios y ejecute
dotnet build
.Ejecute el siguiente comando para generar una migración a fin de crear las tablas de base de datos:
dotnet ef migrations add ModelRevisions --context PizzaContext
Este comando crea una migración denominada: ModelRevisions.
Nota:
Verá este mensaje: Se ha aplicado scaffolding a una operación, lo que puede provocar la pérdida de datos. Revise la precisión de la migración. Este mensaje ha aparecido porque cambió la relación de
Pizza
aTopping
(antes era de uno a varios y ahora es de varios a varios), lo que requiere que se elimine una columna de clave externa existente. Dado que aún no tiene ningún dato en la base de datos, este cambio no es problemático. Pero, por lo general, es aconsejable comprobar la migración generada cuando aparece esta advertencia para asegurarse de que la migración no elimina ni trunca datos.Ejecute el comando siguiente para aplicar la migración ModelRevisions:
dotnet ef database update --context PizzaContext
En la barra de título de la carpeta Explorador de SQLite, seleccione el botón Actualizar la base de datos.
En la carpeta Explorador de SQLite, haga clic con el botón derecho en ContosoPizza.db. Seleccione Mostrar tabla "sqlite_master" para ver el esquema completo de la base de datos y las restricciones.
Importante
La extensión SQLite vuelve a usar las pestañas abiertas de SQLite.
- Se creó una tabla de combinación
PizzaTopping
para representar la relación de varios a varios entre pizzas e ingredientes. - Se agregaron nuevos campos a
Toppings
ySauces
.Calories
se define como una columnatext
porque SQLite no tiene un tipodecimal
coincidente.- De forma similar,
IsVegan
se define como una columnainteger
. SQLite no define un tipobool
. - En ambos casos, EF Core administra la traducción.
- La columna
Name
de cada tabla se ha marcado comonot null
, pero SQLite no tiene ninguna restricciónMaxLength
.
Sugerencia
Los proveedores de bases de datos de EF Core asignan un esquema de modelo a las características de una base de datos específica. Aunque SQLite no implementa una restricción correspondiente para
MaxLength
, otras bases de datos como SQL Server y PostgreSQL sí lo hacen.- Se creó una tabla de combinación
En la carpeta Explorador de SQLite, haga clic con el botón derecho en la tabla
_EFMigrationsHistory
y seleccione Mostrar tabla. La tabla contiene una lista de todas las migraciones aplicadas a la base de datos. Como ha ejecutado dos migraciones, hay dos entradas: una para la migración InitialCreate y otra para ModelRevisions.
Nota:
En este ejercicio se usan atributos de asignación (anotaciones de datos) para asignar modelos a la base de datos. Como alternativa a los atributos de asignación, puede usar la API fluida ModelBuilder para configurar modelos. Ambos enfoques son válidos, pero algunos desarrolladores prefieren un enfoque sobre el otro.
Ha usado migraciones para definir y actualizar un esquema de base de datos. En la siguiente unidad, finalizará los métodos de PizzaService
que manipulan los datos.