Relaciones uno a uno

Las relaciones de uno a uno se usan cuando una entidad está asociada a una entidad, como máximo. Por ejemplo, un elemento Blog tiene un elemento BlogHeader y ese elemento BlogHeader pertenece a un único elemento Blog.

Este documento está estructurado en torno a muchos ejemplos. Los ejemplos comienzan con casos comunes, que también presentan conceptos. En ejemplos posteriores, se tratan tipos menos comunes de configuración. Una buena estrategia aquí es comprender los primeros ejemplos y conceptos y, luego, ir a los últimos ejemplos en función de sus necesidades específicas. Según este enfoque, comenzaremos con relaciones de uno a uno sencillas "obligatorias" y "opcionales".

Sugerencia

El código de todos los ejemplos siguientes se puede encontrar en el archivo OneToOne.cs.

Relación de uno a uno obligatoria

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Una relación de uno a uno consta de:

  • Una o varias propiedades de clave principal o alternativa en la entidad principal. Por ejemplo: Blog.Id.
  • Una o varias propiedades de clave externa en la entidad dependiente. Por ejemplo: BlogHeader.BlogId.
  • Opcionalmente, una navegación de referencia en la entidad principal que hace referencia a la entidad dependiente. Por ejemplo: Blog.Header.
  • Opcionalmente, una navegación de referencia en la entidad dependiente que hace referencia a la entidad principal. Por ejemplo, BlogHeader.Blog.

Sugerencia

No siempre resulta obvio qué lado de una relación de uno a uno debe ser el principal y qué lado debe ser el dependiente. Algunas consideraciones son:

  • Si las tablas de base de datos de los dos tipos ya existen, la tabla con las columnas de clave externa se debe asignar al tipo dependiente.
  • Un tipo suele ser el tipo dependiente si no puede existir lógicamente sin el otro tipo. Por ejemplo, no tiene sentido tener un encabezado para un blog que no existe, por lo que BlogHeader es naturalmente el tipo dependiente.
  • Si hay una relación principal-secundario natural, el elemento secundario suele ser el tipo dependiente.

Por lo tanto, para la relación de este ejemplo:

  • La propiedad de clave externa BlogHeader.BlogId no acepta valores NULL. Esto convierte a la relación en "obligatoria", porque cada entidad dependiente (BlogHeader) debe estar relacionada con alguna principal (Blog), ya que su propiedad de clave externa debe establecerse en algún valor.
  • Ambas entidades tienen navegaciones que apuntan a la entidad relacionada del otro lado de la relación.

Nota

Una relación obligatoria garantiza que todas las entidades dependientes deben estar asociadas a alguna entidad principal. Sin embargo, una entidad principal siempre puede existir sin ninguna entidad dependiente. Es decir, una relación obligatoria no indica que habrá siempre una entidad dependiente. No hay ninguna manera en el modelo de EF, y tampoco hay una manera estándar en una base de datos relacional, de asegurarse de que una entidad principal esté asociada a una dependiente. Si es necesario, se debe implementar en la lógica de la aplicación (lógica de negocios). Para más información, consulte Navegaciones obligatorias.

Sugerencia

Una relación con dos navegaciones, una de la entidad dependiente a la principal y una inversa de la entidad principal a la dependiente, se conoce como relación bidireccional.

Esta relación se detecta por convención. Es decir:

  • Blog se detecta como la entidad principal de la relación y BlogHeader se detecta como la dependiente.
  • BlogHeader.BlogId se detecta como una clave externa de la entidad dependiente que hace referencia a la clave principal Blog.Id de la entidad de seguridad. La relación se detecta como obligatoria porque BlogHeader.BlogId no acepta valores NULL.
  • Blog.BlogHeader se detecta como una navegación de referencia.
  • BlogHeader.Blog se detecta como una navegación de referencia.

Importante

Cuando se usan tipos de referencia que aceptan valores NULL de C#, la navegación desde la entidad dependiente a la principal debe admitir un valor NULL si la propiedad de clave externa admite un valor NULL. Si la propiedad de clave externa no acepta valores NULL, la navegación de referencia puede admitir o no valores NULL. En este caso, BlogHeader.BlogId no admite valores NULL y BlogHeader.Blog tampoco lo hace. La construcción = null!; se usa para marcar este caso como intencional para el compilador de C#, ya que EF normalmente establece la instancia de Blog y no puede ser null en una relación totalmente cargada. Para más información, consulte Tipos de referencia que admiten valores NULL.

En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

En el ejemplo anterior, la configuración de las relaciones comienza por el tipo de entidad principal (Blog). Al igual que con todas las relaciones, es exactamente lo mismo que comenzar por el tipo de entidad dependiente (BlogHeader) en su lugar. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne(e => e.Header)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Ninguna de estas opciones es mejor que la otra; ambas dan como resultado exactamente la misma configuración.

Sugerencia

Nunca es necesario configurar una relación dos veces: una desde la entidad principal y luego otra vez desde la dependiente. Además, el intento de configurar por separado las mitades principal y dependiente de una relación no funciona por norma general. Elija configurar cada relación desde un extremo u otro y, luego, escriba el código de configuración una sola vez.

Relación de uno a uno opcional

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int? BlogId { get; set; } // Optional foreign key property
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Es igual que el ejemplo anterior, salvo que la propiedad de clave externa y la navegación a la entidad principal ahora admiten valores NULL. Esto hace que la relación "opcional" porque un dependiente (BlogHeader) puede no estar relacionado con cualquier entidad de seguridad (Blog) estableciendo su propiedad de clave externa y navegación en null.

Importante

Cuando se usan tipos de referencia que aceptan valores NULL de C#, la propiedad de navegación desde la entidad dependiente a la principal debe admitir un valor NULL si la propiedad de clave externa admite un valor NULL. En este caso, BlogHeader.BlogId admite valores NULL, por lo que BlogHeader.Blog también debe hacerlo. Para más información, consulte Tipos de referencia que admiten valores NULL.

Como antes, esta relación se detecta por convención. En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired(false);
}

Relación de uno a uno obligatoria con relación de clave principal a clave principal

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

A diferencia de las relaciones uno a varios, el extremo dependiente de una relación de uno a uno puede usar su propiedad o propiedades de clave principal como propiedad o propiedades de clave externa. Esto se suele llamar relación de clave principal a clave principal. Esto solo es posible cuando los tipos principal y dependiente tienen los mismos tipos de clave principal, y la relación resultante siempre es obligatoria, ya que la clave principal del tipo dependiente no puede admitir un valor NULL.

Cualquier relación uno a uno en la que la clave externa no se detecte por convención se debe configurar para indicar los extremos principal y dependiente de la relación. Normalmente, esto se realiza con una llamada a HasForeignKey. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>();
}

Sugerencia

También se puede usar HasPrincipalKey para este propósito, pero hacerlo es menos común.

Cuando no se especifica ninguna propiedad en la llamada a HasForeignKey y la clave principal es adecuada, se usa como clave externa. En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.Id)
        .IsRequired();
}

Relación de uno a uno obligatoria con clave externa reemplazada

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

En algunos casos, es posible que no quiera una propiedad de clave externa en el modelo, ya que las claves externas son un detalle de cómo se representa la relación en la base de datos, lo que no es necesario cuando se usa la relación de una manera puramente orientada a objetos. Sin embargo, si las entidades se van a serializar, por ejemplo, para enviarse a través de una conexión, los valores de clave externa pueden ser una manera útil de mantener intacta la información de la relación cuando las entidades no están en forma de objeto. Por lo tanto, suele ser pragmático mantener las propiedades de clave externa en el tipo de .NET para este fin. Las propiedades de clave externa pueden ser privadas, lo que suele ser un buen compromiso para evitar exponer la clave externa, al tiempo que permite que su valor viaje con la entidad.

Siguiendo con el ejemplo anterior, en este ejemplo se elimina la propiedad de clave externa del tipo de entidad dependiente. Sin embargo, en lugar de usar la clave principal, se indica a EF que cree una propiedad de clave externa reemplazada llamada BlogId de tipo int.

Un punto importante que se debe tener en cuenta aquí es que se usan tipos de referencia que admiten valores NULL de C#, por lo que la nulabilidad de la navegación de la entidad dependiente a la principal se usa para determinar si la propiedad de clave externa admite valores NULL y, por tanto, si la relación es opcional u obligatoria. Si no se usan tipos de referencia que admiten valores NULL, la propiedad de clave externa reemplazada admitirá valores NULL de manera predeterminada, lo que hace que la relación sea opcional de manera predeterminada. En este caso, use IsRequired para forzar que la propiedad de clave externa reemplazada no admita valores NULL y así hacer que la relación sea obligatoria.

De nuevo, esta relación necesita alguna configuración para indicar los extremos principal y dependiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId");
}

En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Relación de uno a uno opcional con clave externa reemplazada

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public Blog? Blog { get; set; } // Optional reference navigation to principal
}

Al igual que en el ejemplo anterior, se ha quitado la propiedad de clave externa del tipo de entidad dependiente. Sin embargo, a diferencia del ejemplo anterior, esta vez se crea la propiedad de clave externa como que admite valores NULL porque se usan tipos de referencia que admiten valores NULL de C# y la navegación en el tipo de entidad dependiente admite valores NULL. Esto hace que la relación sea opcional.

Cuando no se usan tipos de referencia que admiten valores NULL de C#, la propiedad de clave externa se creará de manera predeterminada como que admite valores NULL. Esto significa que las relaciones con las propiedades reemplazadas creadas automáticamente son opcionales de forma predeterminada.

Como antes, esta relación necesita alguna configuración para indicar los extremos principal y dependiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId");
}

En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired(false);
}

Relación de uno a uno sin navegación a la entidad principal

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

En este ejemplo, se ha vuelto a introducir la propiedad de clave externa, pero se ha quitado la navegación en la entidad dependiente.

Sugerencia

Una relación con solo una navegación, una de la entidad dependiente a la principal o una de la entidad principal a la dependiente, pero no ambas, se conoce como relación unidireccional.

Esta relación se detecta por convención, ya que se detecta la clave externa, lo que indica el lado dependiente. En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Observe que la llamada a WithOne no tiene argumentos. Esta es la manera de indicar a EF que no hay navegación de BlogHeader a Blog.

Si la configuración comienza desde la entidad sin navegación, el tipo de la entidad del otro extremo de la relación se debe especificar explícitamente mediante la llamada genérica HasOne<>(). Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne<Blog>()
        .WithOne(e => e.Header)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Relación de uno a uno sin navegación a la entidad principal y con clave externa reemplazada

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
}

En este ejemplo, se combinan dos de los ejemplos anteriores, de forma que se quita la propiedad de clave externa y la navegación de la entidad dependiente.

Como antes, esta relación necesita alguna configuración para indicar los extremos principal y dependiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Se puede usar una configuración más completa para configurar explícitamente el nombre de la navegación y de la clave externa, con una llamada adecuada a IsRequired() o a IsRequired(false) según sea necesario. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne()
        .HasForeignKey<BlogHeader>("BlogId")
        .IsRequired();
}

Relación de uno a uno sin navegación a la entidad dependiente

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Los dos ejemplos anteriores tenían navegaciones de la entidad principal a la dependiente, pero no había navegación de la dependiente a la principal. En los dos ejemplos siguientes, se vuelve a introducir la navegación en la entidad dependiente, mientras que se quita la navegación en la principal.

Por convención, EF tratará esto como una relación de uno a varios. Se necesita una configuración mínima para que sea una relación de uno a uno:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne();
}

Observe de nuevo que se llama a WithOne() sin argumentos para indicar que no hay navegación en esta dirección.

En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BlogHeader>()
        .HasOne(e => e.Blog)
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Si la configuración comienza desde la entidad sin navegación, el tipo de la entidad del otro extremo de la relación se debe especificar explícitamente mediante la llamada genérica HasOne<>(). Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne<BlogHeader>()
        .WithOne(e => e.Blog)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Relación de uno a uno sin navegación

En ocasiones, puede ser útil configurar una relación sin navegaciones. Esta relación solo se puede manipular cambiando directamente el valor de clave externa.

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
}

Esta relación no se detecta por convención, ya que no hay ninguna navegación que indique que los dos tipos están relacionados. Se puede configurar explícitamente en OnModelCreating. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne<BlogHeader>()
        .WithOne();
}

Con esta configuración, la propiedad BlogHeader.BlogId se sigue detectando como clave externa por convención y la relación es "obligatoria" porque la propiedad de clave externa no admite valores NULL. La relación se puede convertir en "opcional" haciendo que la propiedad de clave externa admita valores NULL.

Una configuración explícita más completa de esta relación es la siguiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne<BlogHeader>()
        .WithOne()
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Relación de uno a uno con clave alternativa

En todos los ejemplos hasta ahora, la propiedad de clave externa de la entidad dependiente está restringida a la propiedad de clave principal de la entidad principal. En su lugar, la clave externa se puede restringir a una propiedad diferente, que entonces se convierte en una clave alternativa para el tipo de entidad principal. Por ejemplo:

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public int AlternateId { get; set; } // Alternate key as target of the BlogHeader.BlogId foreign key
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Esta relación no se detecta por convención, ya que EF siempre creará, por convención, una relación con la clave principal. Se puede configurar explícitamente en OnModelCreating mediante una llamada a HasPrincipalKey. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasPrincipalKey<Blog>(e => e.AlternateId);
}

HasPrincipalKey se puede combinar con otras llamadas para configurar explícitamente las navegaciones, las propiedades de clave externa y la naturaleza obligatoria u opcional. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .HasPrincipalKey<Blog>(e => e.AlternateId)
        .HasForeignKey<BlogHeader>(e => e.BlogId)
        .IsRequired();
}

Relación de uno a uno con clave externa compuesta

En todos los ejemplos hasta ahora, la propiedad de clave principal o alternativa de la entidad principal consta de una sola propiedad. Las claves principales o alternativas también se pueden formar con más de una propiedad, y se conocen como "claves compuestas". Cuando la entidad principal de una relación tiene una clave compuesta, la clave externa de la entidad dependiente también debe ser una clave compuesta con el mismo número de propiedades. Por ejemplo:

// Principal (parent)
public class Blog
{
    public int Id1 { get; set; } // Composite key part 1
    public int Id2 { get; set; } // Composite key part 2
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId1 { get; set; } // Required foreign key property part 1
    public int BlogId2 { get; set; } // Required foreign key property part 2
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Esta relación se detecta por convención. Sin embargo, solo se detectará si la clave compuesta se ha configurado explícitamente, ya que las claves compuestas no se detectan automáticamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasKey(e => new { e.Id1, e.Id2 });
}

Importante

Se considera que un valor de clave externa compuesta es null si alguno de sus valores de propiedad es NULL. Una clave externa compuesta con una propiedad NULL y otra que no sea NULL no se considerará una coincidencia en el caso de una clave principal o alternativa con los mismos valores. Ambas se considerarán null.

Tanto HasForeignKey como HasPrincipalKey se pueden usar para especificar explícitamente claves con varias propiedades. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>(
        nestedBuilder =>
        {
            nestedBuilder.HasKey(e => new { e.Id1, e.Id2 });

            nestedBuilder.HasOne(e => e.Header)
                .WithOne(e => e.Blog)
                .HasPrincipalKey<Blog>(e => new { e.Id1, e.Id2 })
                .HasForeignKey<BlogHeader>(e => new { e.BlogId1, e.BlogId2 })
                .IsRequired();
        });
}

Sugerencia

En el código anterior, las llamadas a HasKey y HasOne se han agrupado en un generador anidado. Los generadores anidados eliminan la necesidad de llamar a Entity<>() varias veces cuando el tipo de entidad es el mismo, pero son funcionalmente equivalentes a llamar a Entity<>() varias veces.

Relación de uno a uno obligatoria sin eliminación en cascada

// Principal (parent)
public class Blog
{
    public int Id { get; set; }
    public BlogHeader? Header { get; set; } // Reference navigation to dependent
}

// Dependent (child)
public class BlogHeader
{
    public int Id { get; set; }
    public int BlogId { get; set; } // Required foreign key property
    public Blog Blog { get; set; } = null!; // Required reference navigation to principal
}

Por convención, las relaciones obligatorias están configuradas para la eliminación en cascada. Esto se debe a que la entidad dependiente no puede existir en la base de datos una vez eliminada la entidad principal. La base de datos puede estar configurada para generar un error, que normalmente bloquea la aplicación, en lugar de eliminar automáticamente las filas dependientes que ya no pueden existir. Esto requiere alguna configuración:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .HasOne(e => e.Header)
        .WithOne(e => e.Blog)
        .OnDelete(DeleteBehavior.Restrict);
}

Relación de uno a uno con referencia propia

En todos los ejemplos anteriores, el tipo de entidad principal era diferente del tipo de entidad dependiente. Pero no siempre es así. Por ejemplo, en los tipos siguientes, cada elemento Person está opcionalmente relacionado con otro elemento Person.

public class Person
{
    public int Id { get; set; }

    public int? HusbandId { get; set; } // Optional foreign key property
    public Person? Husband { get; set; } // Optional reference navigation to principal
    public Person? Wife { get; set; } // Reference navigation to dependent
}

Esta relación se detecta por convención. En los casos en los que las navegaciones, la clave externa o la naturaleza obligatoria-opcional de la relación no se detecten por convención, estos elementos se pueden configurar explícitamente. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Person>()
        .HasOne(e => e.Husband)
        .WithOne(e => e.Wife)
        .HasForeignKey<Person>(e => e.HusbandId)
        .IsRequired(false);
}

Nota

En el caso de las relaciones de uno a uno con referencia propia, ya que los tipos de entidad principal y dependiente son los mismos, especificar qué tipo contiene la clave externa no aclara el extremo dependiente. En este caso, la navegación especificada en HasOne apunta desde la entidad dependiente a la principal y la navegación especificada en WithOne apunta de la principal a la dependiente.