Modelos y Fluent API en Entity Framework

Resumen

Crear modelos en Entity Framework Core es el paso que convierte tu API en una aplicación capaz de guardar y recuperar datos reales. Aquí aprendes a definir entidades, configurar tablas con Fluent API y establecer relaciones entre usuarios y tareas para construir un mini proyecto funcional.

Cómo defines los modelos User y TaskItem en una API

Todo empieza creando una carpeta llamada Models dentro del proyecto. Dentro vas a alojar las dos clases que representarán tus tablas: una para los usuarios y otra para las tareas que cada usuario tendrá asignadas.

Qué propiedades necesita la clase User

El modelo User arranca con lo esencial. Defines public int Id, luego public string Name y public string Email. A las propiedades de tipo string conviene agregarles un valor por defecto con = string.Empty; para evitar advertencias del compilador sobre valores nulos.

Si quieres robustecer el modelo, puedes sumar campos como dirección o teléfono, datos que no se relacionan con tareas pero enriquecen el perfil del usuario.

Qué propiedades lleva la clase TaskItem

La clase TaskItem repite el patrón: un Id, un Title con valor por defecto, y un campo booleano IsCompleted para saber si la tarea está terminada.

La clave de este modelo está en la relación. Agregas UserId para guardar el identificador del usuario asignado, y luego una propiedad de navegación public User? User { get; set; }. El signo de interrogación permite que sea nula, lo que te facilita consultar una tarea junto con la información completa de su usuario cuando lo necesites.

¿Qué es una propiedad de navegación en Entity Framework? Es una propiedad que apunta a otra entidad relacionada y te permite acceder a sus datos directamente desde la consulta, sin escribir joins manuales.

Cómo configuras AppDbContext con DbSet

Con las clases listas, abres el archivo AppDbContext y registras cada modelo como un DbSet. Esto le indica a Entity Framework que debe crear una tabla por cada entidad cuando se aplique la migración a la base de datos.

  • public DbSet<User> Users { get; set; } para la colección de usuarios.
  • public DbSet<TaskItem> Tasks { get; set; } para la colección de tareas.

Usar el plural en Users y Tasks es una convención que comunica con claridad que ahí vive la colección completa de registros, no un único elemento.

Cómo personalizas tablas y columnas con Fluent API

Los DbSet ya bastan para generar tablas, pero si quieres controlar nombres, tamaños, valores por defecto y relaciones, necesitas el método OnModelCreating. Lo declaras como protected override void OnModelCreating(ModelBuilder modelBuilder) y desde ahí configuras cada entidad con expresiones lambda.

Cómo configurar la entidad User

Dentro de modelBuilder.Entity<User>(entity => { ... }) aplicas estos métodos en cadena:

  • ToTable("Users") define el nombre exacto de la tabla.
  • HasKey(u => u.Id) declara la clave primaria.
  • Property(u => u.Name).IsRequired().HasMaxLength(100) marca el nombre como obligatorio y limita su longitud a 100 caracteres.
  • Property(u => u.Email).IsRequired().HasMaxLength(200) hace lo mismo con el correo, ampliándolo a 200 caracteres.

Cada propiedad se traduce en una columna, y Fluent API deja explícito qué representa cada una en la base de datos.

¿Para qué sirve Fluent API en Entity Framework? Te permite configurar tablas, columnas, restricciones y relaciones desde código C# sin depender de atributos sobre cada propiedad, manteniendo el modelo limpio.

Cómo configurar TaskItem y su relación con User

La entidad TaskItem repite la estructura, pero suma dos elementos clave. Para IsCompleted usas HasDefaultValue(false), garantizando que toda tarea nueva nazca como no completada hasta que alguien la marque.

La relación se define así:

csharp entity.HasOne(t => t.User) .WithMany(u => u.Tasks) .HasForeignKey(t => t.UserId);

Esto le dice a Entity Framework que un usuario puede tener muchas tareas y que UserId es la clave foránea que conecta ambas tablas.

Por qué necesitas una colección de tareas en User

Al declarar WithMany(u => u.Tasks) el compilador exige que la clase User exponga esa colección. La agregas como public ICollection<TaskItem> Tasks { get; set; } = new List<TaskItem>();.

Inicializarla con una lista vacía evita errores de referencia nula y asegura que siempre puedas iterar sobre las tareas de un usuario, incluso si todavía no tiene ninguna asignada.

¿Por qué inicializar una colección con new List? Porque garantiza que la propiedad nunca sea null, evitando excepciones cuando recorres la lista antes de agregar elementos.

Con esta configuración tienes dos modelos relacionados, tablas personalizadas y restricciones claras a nivel de base de datos. ¿Te animas a sumar una propiedad Role al modelo User y configurarla con Fluent API? Cuéntame en los comentarios cómo la implementaste.