entity-framework-code-first

Code First en Entity Framework

  • 6 min

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:

  1. Definir el modelo: Creamos clases que representan nuestros datos (entidades y DbContext).
  2. Configuración de relaciones: Configuramos relaciones y restricciones mediante código
  3. Migraciones: Generar y aplicar migraciones para crear o actualizar la base de datos.
  4. 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 llamadas Id o NombreDeClaseId como claves primarias.
  • Nombre, Precio y Stock 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 un DbSet que representa la tabla Productos.
  • 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) y muchos a muchos (N). Estas relaciones se configuran mediante Data Annotations o Fluent API.

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 relaciona Producto con Categoria.
  • 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étodos Up() y Down().
  • 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() y Down().

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

ComandoDescripción
dotnet ef migrations add NombreCrea una nueva migración con cambios detectados.
dotnet ef database updateAplica todas las migraciones pendientes.
dotnet ef migrations removeElimina la última migración (si aún no se ha aplicado).
dotnet ef database update NombreMigracionAplica hasta una migración específica.
dotnet ef database update 0Revierte 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.