Code-First es un enfoque de desarrollo en Entity Framework donde los modelos de datos se definen mediante clases de C#.
En este modelo, partimos de las clases de C# para definir nuestra estructura de datos. Estas clases (que llamamos entidades), representan las tablas de la base de datos, y sus propiedades representan las columnas.
Posteriormente, Entity Framework se encarga de generar automáticamente la base de datos correspondiente (incluyendo tablas, relaciones y restricciones) partir de estas entidades.
Este enfoque es especialmente útil en proyectos nuevos, donde no existe una base de datos predefinida, o en proyectos donde se prefiere mantener el control total sobre el diseño de la base de datos mediante código.
Esto contrasta con el enfoque Database First, donde se parte de una base de datos existente y se generan las clases automáticamente.
Flujo de trabajo con Code-First
El flujo de trabajo con Code-First consta de los siguientes pasos:
- Definir el modelo: Creamos clases que representan nuestros datos (entidades y DbContext).
- Configuración de relaciones: Configuramos relaciones y restricciones mediante código
- Migraciones: Generar y aplicar migraciones para crear o actualizar la base de datos.
- Operaciones: Realizar operaciones como haríamos normalmente.
El modelo de datos
Hemos visto cómo crear el modelo, relaciones, y operaciones, en el resto del curso. Así que no voy a entretenerme demasiado aquí
Pero, a modo de resumen, supongamos que tenemos un modelo como este
Las entidades son clases de C# que representan las tablas de la base de datos. Cada propiedad de la clase representa una columna en la tabla.
Por ejemplo, si queremos crear una tabla Productos
, definimos la siguiente clase:
public class Producto
{
public int Id { get; set; } // Clave primaria
public string Nombre { get; set; }
public decimal Precio { get; set; }
public int Stock { get; set; }
}
En este ejemplo:
Id
es la clave primaria de la tabla. Entity Framework reconoce automáticamente las propiedades llamadasId
oNombreDeClaseId
como claves primarias.Nombre
,Precio
yStock
son columnas de la tabla.
El contexto es una clase que hereda de DbContext
y actúa como un puente entre las entidades y la base de datos. Esta clase contiene propiedades de tipo DbSet<T>
, que representan las tablas de la base de datos.
public class MiContexto : DbContext
{
public DbSet<Producto> Productos { get; set; } // Representa la tabla Productos
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// Configura la conexión a la base de datos
optionsBuilder.UseSqlServer("Server=miServidor;Database=MiBaseDeDatos;Trusted_Connection=True;");
}
}
En este ejemplo:
Productos
es unDbSet
que representa la tablaProductos
.OnConfiguring
se utiliza para configurar la cadena de conexión a la base de datos.
Entity Framework permite definir relaciones entre entidades, como relaciones uno a uno (1:1), uno a muchos (1
Supongamos que queremos agregar una relación entre Producto
y Categoria
. Un producto pertenece a una categoría, y una categoría puede tener muchos productos.
public class Categoria
{
public int Id { get; set; }
public string Nombre { get; set; }
public ICollection<Producto> Productos { get; set; } // Relación 1:N
}
public class Producto
{
public int Id { get; set; }
public string Nombre { get; set; }
public decimal Precio { get; set; }
public int Stock { get; set; }
public int CategoriaId { get; set; } // Clave foránea
public Categoria Categoria { get; set; } // Relación 1:N
}
En este ejemplo:
CategoriaId
es la clave foránea que relacionaProducto
conCategoria
.Categoria
es una propiedad de navegación que permite acceder a la categoría asociada a un producto.
Migraciones
Las migraciones son un mecanismo que permite generar y aplicar cambios en la base de datos a partir de las entidades definidas en el código.
Cada migración es un registro versionado de cambios realizados en el modelo de datos de la aplicación, que refleja un conjunto de modificaciones a aplicar a la base de datos (agregar o eliminar tablas, campos, restricciones, etc.)
Para crear una migración, utilizamos la CLI de .NET (dotnet ef
)
Antiguamente se usaban comandos de Package Manager Console como Add-Migration
, o Update-Database
. Actualmente están en desuso, por lo que si los veis, es que es documentación antigua.
Creación de la primera migración
Para crear la primera migración, abrimos una terminal en la carpeta del proyecto y ejecutamos:
dotnet ef migrations add Inicial
Esto genera una carpeta Migrations/
con tres archivos:
20250407123456_Inicial.cs
→ Clase con métodosUp()
yDown()
.EscuelaContextModelSnapshot.cs
→ Refleja el estado actual del modelo.- Un archivo adicional de configuración si es necesario.
¿Qué contiene una migración?
Una migración en EF es una clase C# que contiene instrucciones para modificar la base de datos, tanto para aplicar los cambios (Up
) como para deshacerlos (Down
).
La clase Inicial
contiene métodos como estos:
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Estudiantes",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Nombre = table.Column<string>(nullable: true),
Correo = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Estudiantes", x => x.Id);
});
}
Y el método Down
hace lo contrario:
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(name: "Estudiantes");
}
- No generes migraciones automáticas sin revisarlas. Siempre valida qué va a hacer
Up()
yDown()
.
Aplicar las migraciones a la base de datos
Una vez creada la migración, ejecutamos:
dotnet ef database update
Esto aplica las instrucciones Up()
en la base de datos. Si hay un error, la transacción se revierte.
EF crea automáticamente una tabla __EFMigrationsHistory
donde guarda qué migraciones han sido aplicadas. Esto permite aplicar solo las nuevas y evitar duplicados.
Cambios en el modelo y migraciones
Supongamos que agregamos una nueva entidad:
public class Curso
{
public int Id { get; set; }
public string Titulo { get; set; }
public int Creditos { get; set; }
}
Y la añadimos al DbContext
:
public DbSet<Curso> Cursos { get; set; }
Generamos la migración:
dotnet ef migrations add AgregaCurso
Y luego:
dotnet ef database update
EF aplicará solo los cambios mínimos y necesarios. En este caso, crear la nueva tabla Cursos
.
Comandos útiles de migración
Comando | Descripción |
---|---|
dotnet ef migrations add Nombre | Crea una nueva migración con cambios detectados. |
dotnet ef database update | Aplica todas las migraciones pendientes. |
dotnet ef migrations remove | Elimina la última migración (si aún no se ha aplicado). |
dotnet ef database update NombreMigracion | Aplica hasta una migración específica. |
dotnet ef database update 0 | Revierte todas las migraciones (vuelve al esquema vacío). |
¿Qué no hacen las migraciones?
- No manejan datos. Si necesitas insertar información, debes escribir código adicional (por ejemplo, en
Up()
). - No detectan algunos cambios “sutiles”, como renombrar una propiedad (lo interpretan como eliminación + creación).
- No sincronizan datos en producción. En muchos entornos, estas migraciones deben revisarse o aplicarse manualmente.