entity-framework-relaciones-uno-a-uno

Relaciones uno a uno en Entity Framework

  • 4 min

En el diseño de bases de datos, una relación uno a uno (1:1) ocurre cuando un registro en una tabla está asociado con exactamente un registro en otra tabla.

Este tipo de relación es útil para segmentación de información (por ejemplo, entidades que están íntimamente relacionadas, pero son cosas diferentes).

Supongamos que tenemos dos entidades:

  • User
  • Profile

Cada usuario tiene exactamente un perfil, y cada perfil pertenece a un solo usuario.

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }         // Para login
    public string Email { get; set; }            // Para autenticación
    public string PasswordHash { get; set; }     // Seguridad

    public Profile Profile { get; set; } // Propiedad de navegación
}

public class Profile
{
    public int Id { get; set; }
    public string FullName { get; set; }         // Nombre completo
    public DateTime BirthDate { get; set; }      // Fecha de nacimiento
    public string Bio { get; set; }              // Biografía corta

    public User User { get; set; } // Propiedad de navegación
}

¿Por qué no poner todo en User?

En este ejemplo podemos decidir tener las entidades separadas por segregación de datos (es decir, por limpieza)

  • User contiene solo la información necesaria para la autenticación y seguridad del sistema,
  • Profile, en cambio, guarda datos sobre la persona asociada al User

Otro ejemplo claro lo verás en una relación 1:1 podría ser Empleado y Ordenador. Aunque un empleado pueda tener un solo ordenador, y un ordenador una única persona, son entidades separadas.

Configurar la relación

Convenciones de nombres

Entity Framework puede inferir la relación 1:1 si:

  • Ambas entidades tienen una propiedad de navegación que apunta a la otra.
  • Una de las entidades tiene una clave foránea que sigue la convención {NombreClavePrimaria}Id.
public class Profile
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int UserId { get; set; } // Clave foránea (UserId → User.Id)
    public User User { get; set; } // Propiedad de navegación
}

EF detecta automáticamente que Profile.UserId es clave foránea de User.Id

Con Data Annotations

Si no seguimos las convenciones, podemos usar atributos:

[ForeignKey] en la clave foránea

public class Profile
{
    public int Id { get; set; }
    public string FullName { get; set; }

    [ForeignKey("User")] // Indica que UserId es FK de User
    public int UserId { get; set; }

    public User User { get; set; }
}

[ForeignKey] en la propiedad de navegación

public class Profile
{
    public int Id { get; set; }
    public string FullName { get; set; }
		
		
    public int UserId { get; set; }

    [ForeignKey("UserId")] // Indica que UserId es la FK
    public User User { get; set; }
}

Con Fluent API

O también podemos confgurarlo con Fluent API.

Clave foránea en Profile

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .HasOne(u => u.Profile)       // Un User tiene un Profile
        .WithOne(p => p.User)         // Un Profile pertenece a un User
        .HasForeignKey<Profile>(p => p.UserId); // FK en Profile
}

Clave foránea en User (menos común)

modelBuilder.Entity<Profile>()
    .HasOne(p => p.User)
    .WithOne(u => u.Profile)
    .HasForeignKey<User>(u => u.ProfileId); // FK en User

Estructura de la base de datos generada

Al aplicar una migración, EF crea las tablas:

CREATE TABLE Users (
    Id INT PRIMARY KEY IDENTITY,
    Username NVARCHAR(100)
);

CREATE TABLE Profiles (
    Id INT PRIMARY KEY IDENTITY,
    FullName NVARCHAR(100),
    UserId INT UNIQUE, -- ¡UNIQUE garantiza la relación 1:1!
    FOREIGN KEY (UserId) REFERENCES Users(Id)
);

UserId en Profiles es UNIQUE, lo que evita que un mismo usuario tenga múltiples perfiles

Ejemplos practicos