Отношения «один-к-одному»
Связи "один к одному" используются, когда одна сущность связана с одной другой сущностью. Например, имеет Blog
один BlogHeader
объект, который BlogHeader
принадлежит одному Blog
.
Этот документ структурирован вокруг множества примеров. Примеры начинаются с распространенных случаев, которые также содержат основные понятия. В последующих примерах рассматриваются менее распространенные виды конфигурации. Хороший подход заключается в том, чтобы понять первые несколько примеров и концепций, а затем перейти к более поздним примерам на основе конкретных потребностей. На основе этого подхода мы начнем с простых "обязательных" и "необязательных" связей "один к одному".
Совет
Код для всех приведенных ниже примеров можно найти в OneToOne.cs.
Обязательный один к одному
// 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
}
Связь "один к одному" состоит из:
- Одно или несколько первичных или альтернативных ключевых свойств основной сущности. Например,
Blog.Id
. - Одно или несколько свойств внешнего ключа для зависимой сущности. Например,
BlogHeader.BlogId
. - При необходимости ссылка навигации по основной сущности, ссылающейся на зависимые сущности. Например,
Blog.Header
. - При необходимости ссылка навигации по зависимой сущности, ссылающейся на основную сущность. Например,
BlogHeader.Blog
.
Совет
Не всегда очевидно, какая сторона отношения "один к одному" должна быть основной, и какая сторона должна быть зависимой. Ниже приведены некоторые рекомендации.
- Если таблицы базы данных для двух типов уже существуют, то таблица со столбцами внешнего ключа должна сопоставляться с зависимым типом.
- Тип обычно является зависимым типом, если он не может логически существовать без другого типа. Например, нет смысла иметь заголовок для блога, который не существует, поэтому,
BlogHeader
естественно, зависимый тип. - Если существует естественная связь родительского или дочернего, то дочерний тип обычно является зависимым.
Таким образом, для связи в этом примере:
- Свойство
BlogHeader.BlogId
внешнего ключа не допускает значение NULL. Это делает связь "обязательной", так как каждое зависимое (BlogHeader
) должно быть связано с некоторым субъектом (Blog
), так как его свойство внешнего ключа должно иметь некоторое значение. - Обе сущности имеют навигации, указывающие на связанную сущность с другой стороны связи.
Примечание.
Необходимая связь гарантирует, что каждая зависимые сущности должна быть связана с какой-либо основной сущностью. Однако основная сущность всегда может существовать без какой-либо зависимой сущности. То есть требуемая связь не указывает на то, что всегда будет зависимый объект. В модели EF нет никакого способа, а также нет стандартного способа в реляционной базе данных, чтобы убедиться, что субъект связан с зависимым. Если это необходимо, она должна быть реализована в логике приложения (бизнес). Дополнительные сведения см. в разделе "Обязательные навигации ".
Совет
Связь с двумя навигациями —один из зависимых от субъекта и обратного от субъекта к зависимому — называется двунаправленной связью.
Эта связь обнаруживается по соглашению. Это означает следующее.
Blog
обнаруживается как субъект в связи иBlogHeader
обнаруживается как зависимый.BlogHeader.BlogId
обнаруживается как внешний ключ зависимого, ссылающегосяBlog.Id
на первичный ключ субъекта. Связь обнаруживается по мере необходимости, так какBlogHeader.BlogId
не допускает значение NULL.Blog.BlogHeader
обнаруживается как эталонная навигация.BlogHeader.Blog
обнаруживается как эталонная навигация.
Важно!
При использовании ссылочных типов, допускающих значение NULL C#, навигация от зависимого от субъекта должна иметь значение NULL, если свойство внешнего ключа допускает значение NULL. Если свойство внешнего ключа не допускает значение NULL, то навигация может иметь значение NULL или нет. В этом случае BlogHeader.BlogId
не допускается значение NULL, а BlogHeader.Blog
также не допускает значение NULL. Конструкция = null!;
используется для того, чтобы пометить это как преднамеренное для компилятора C#, так как EF обычно задает Blog
экземпляр и не может иметь значение NULL для полностью загруженной связи. Дополнительные сведения см. в статье о работе с типами ссылок, допускающих значение NULL.
В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
В приведенном выше примере конфигурация связей запускает тип основной сущности (Blog
). Как и во всех отношениях, вместо этого он точно эквивалентен началу с типа зависимой сущности (BlogHeader
). Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne(e => e.Blog)
.WithOne(e => e.Header)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Ни один из этих вариантов лучше, чем другой; они оба приводят к точно той же конфигурации.
Совет
Никогда не нужно настраивать связь дважды, начиная с субъекта, а затем снова начинаться с зависимого. Кроме того, попытка настроить основной и зависимый половины связи отдельно не работает. Выберите настроить каждую связь из одного конца или другого, а затем написать код конфигурации только один раз.
Необязательный один к одному
// 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
}
Это то же, что и предыдущий пример, за исключением того, что свойство внешнего ключа и навигация к субъекту теперь допускает значение NULL. Это делает связь "необязательным", так как зависимость (BlogHeader
) не может быть связана с любым субъектом (Blog
) путем задания свойства внешнего ключа и навигации.null
Важно!
При использовании ссылочных типов, допускающих значение NULL C#, свойство навигации от зависимого от субъекта должно иметь значение NULL, если свойство внешнего ключа допускает значение NULL. В этом случае BlogHeader.BlogId
допускается значение NULL, поэтому BlogHeader.Blog
также необходимо иметь значение NULL. Дополнительные сведения см. в статье о работе с типами ссылок, допускающих значение NULL.
Как и раньше, эта связь обнаруживается по соглашению. В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired(false);
}
Обязательный один к одному с первичным ключом для связи первичного ключа
// 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
}
В отличие от связей "один ко многим", зависимый конец отношения "один к одному" может использовать его основное свойство или свойства в качестве свойства внешнего ключа или свойств. Это часто называется связью PK-to-PK. Это возможно только в том случае, если основной и зависимый типы имеют одинаковые типы первичных ключей, и результирующая связь всегда требуется, так как первичный ключ зависимого не может иметь значение NULL.
Любая связь "один к одному", в которой внешний ключ не обнаруживается по соглашению, должна быть настроена, чтобы указать субъект и зависимые концы отношения. Обычно это делается с вызовом HasForeignKey
. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>();
}
Совет
HasPrincipalKey
также может использоваться для этой цели, но это менее распространено.
Если свойство не указано в вызове HasForeignKey
, а первичный ключ подходит, он используется в качестве внешнего ключа. В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.Id)
.IsRequired();
}
Обязательный один к одному с теневым внешним ключом
// 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
}
В некоторых случаях в модели может не потребоваться свойство внешнего ключа, так как внешние ключи представляют собой подробные сведения о том, как связь представлена в базе данных, которая не требуется при использовании связи исключительно объектно-ориентированной. Однако если сущности будут сериализованы, например для отправки по проводу, то значения внешнего ключа могут быть полезным способом сохранения сведений о связи, если сущности не находятся в форме объекта. Поэтому часто прагматично сохранять свойства внешнего ключа в типе .NET для этой цели. Свойства внешнего ключа могут быть закрытыми, что часто является хорошим компромиссом, чтобы избежать предоставления внешнего ключа, позволяя ему перемещаться с сущностью.
После предыдущего примера этот пример удаляет свойство внешнего ключа из типа зависимой сущности. Однако вместо использования первичного ключа EF вместо этого предписывается создать теневое свойство внешнего ключа, называемое BlogId
типом int
.
Важно отметить, что используются ссылочные типы , допускающие значение NULL, C#, поэтому для определения того, является ли свойство внешнего ключа пустым, используется значение NULL, поэтому связь является необязательной или обязательной. Если ссылочные типы, допускающие значение NULL, не используются, свойство теневого внешнего ключа будет иметь значение NULL по умолчанию, что делает связь необязательной по умолчанию. В этом случае используйте для IsRequired
принудительного принудительного применения теневое свойство внешнего ключа к ненулевому значению и обязательной связи.
Эта связь снова требует некоторой конфигурации, чтобы указать субъект и зависимые концы:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId");
}
В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired();
}
Необязательный один к одному с теневым внешним ключом
// 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
}
Как и в предыдущем примере, свойство внешнего ключа было удалено из типа зависимой сущности. Однако, в отличие от предыдущего примера, на этот раз свойство внешнего ключа создается как значение NULL, так как используются ссылочные типы C#, допускающие значение NULL, и навигация по типу зависимой сущности допускает значение NULL. Это делает связь необязательной.
Если ссылочные типы ссылок на C# не используются, свойство внешнего ключа по умолчанию будет создано как допустимое значение NULL. Это означает, что отношения с автоматически созданными свойствами тени являются необязательными по умолчанию.
Как и раньше, эта связь должна иметь определенную конфигурацию, чтобы указать основные и зависимые концы:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId");
}
В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired(false);
}
Один к одному без перехода к субъекту
// 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
}
В этом примере свойство внешнего ключа было повторно введено, но навигация по зависимому элементу была удалена.
Совет
Связь только с одной навигацией—один из зависимых от субъекта или одного из зависимых, но не обоих— называется однонаправленной связью.
Эта связь обнаруживается по соглашению, так как внешний ключ обнаруживается таким образом, что указывает на зависимой стороне. В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne()
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Обратите внимание, что вызов WithOne
не имеет аргументов. Это способ сказать EF, что навигация не выполняется.BlogHeader
Blog
Если конфигурация начинается с сущности без навигации, то тип сущности в другом конце связи должен быть явно указан с помощью универсального HasOne<>()
вызова. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne<Blog>()
.WithOne(e => e.Header)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Один к одному без перехода к субъекту и с теневым внешним ключом
// 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; }
}
Этот пример объединяет два из предыдущих примеров, удаляя как свойство внешнего ключа, так и навигацию по зависимым.
Как и раньше, эта связь должна иметь определенную конфигурацию, чтобы указать основные и зависимые концы:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne()
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired();
}
Более полную конфигурацию можно использовать для явной настройки имени навигации и внешнего ключа с соответствующим вызовом IsRequired()
или IsRequired(false)
по мере необходимости. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne()
.HasForeignKey<BlogHeader>("BlogId")
.IsRequired();
}
Один к одному без навигации для зависимой
// 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
}
В предыдущих двух примерах были переходы от субъекта к зависимым, но переход от зависимого к субъекту отсутствует. Для следующих нескольких примеров навигация по зависимому объекту повторно представлена, а навигация по субъекту удаляется.
По соглашению EF будет рассматривать это как связь "один ко многим". Для его создания требуется некоторая минимальная конфигурация:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne(e => e.Blog)
.WithOne();
}
Обратите внимание еще раз, что вызывается без аргументов, WithOne()
чтобы указать, что навигация в этом направлении отсутствует.
В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogHeader>()
.HasOne(e => e.Blog)
.WithOne()
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Если конфигурация начинается с сущности без навигации, то тип сущности в другом конце связи должен быть явно указан с помощью универсального HasOne<>()
вызова. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne<BlogHeader>()
.WithOne(e => e.Blog)
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Один к одному без навигаций
Иногда это может быть полезно для настройки связи без навигации. Такая связь может управляться только путем непосредственного изменения значения внешнего ключа.
// 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
}
Эта связь не обнаруживается по соглашению, так как навигации отсутствуют, указывающие на то, что два типа связаны. Его можно настроить явно в OnModelCreating
. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne<BlogHeader>()
.WithOne();
}
В этой конфигурации BlogHeader.BlogId
свойство по-прежнему обнаруживается как внешний ключ по соглашению, и отношение является обязательным, так как свойство внешнего ключа не допускает значение NULL. Отношение можно сделать необязательным, сделав свойство внешнего ключа пустым.
Более полная явная конфигурация этой связи:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne<BlogHeader>()
.WithOne()
.HasForeignKey<BlogHeader>(e => e.BlogId)
.IsRequired();
}
Один к одному с альтернативным ключом
В всех примерах до сих пор свойство внешнего ключа зависимостей ограничивается свойством первичного ключа субъекта. Внешний ключ вместо этого может быть ограничен другим свойством, которое затем становится альтернативным ключом для типа основной сущности. Например:
// 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
}
Эта связь не обнаруживается по соглашению, так как EF всегда будет создавать связь с первичным ключом. Его можно настроить явно с OnModelCreating
помощью вызова HasPrincipalKey
. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.HasPrincipalKey<Blog>(e => e.AlternateId);
}
HasPrincipalKey
можно объединить с другими вызовами, чтобы явно настроить навигации, свойства внешнего ключа и обязательный или необязательный характер. Например:
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();
}
Один к одному с составным внешним ключом
В всех примерах до сих пор основное или альтернативное свойство ключа субъекта состоит из одного свойства. Первичные или альтернативные ключи также могут быть сформированы в форме нескольких свойств. Они называются составными ключами. Если субъект связи имеет составной ключ, внешний ключ зависимого также должен быть составным ключом с таким же количеством свойств. Например:
// 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
}
Эта связь обнаруживается по соглашению. Однако оно будет обнаружено только в том случае, если составной ключ настроен явным образом, так как составные ключи не обнаруживаются автоматически. Например:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasKey(e => new { e.Id1, e.Id2 });
}
Важно!
Составное значение внешнего ключа считается null
равным, если какие-либо из его значений свойств имеют значение NULL. Составной внешний ключ с одним свойством NULL и другим, отличным от NULL, не будет считаться совпадением для первичного или альтернативного ключа с теми же значениями. Оба будут рассматриваться null
.
HasPrincipalKey
Оба HasForeignKey
и могут использоваться для явного указания ключей с несколькими свойствами. Например:
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();
});
}
Совет
В приведенном выше коде вызовы HasKey
и HasOne
были сгруппированы в вложенный построитель. Вложенные построители удаляют необходимость Entity<>()
вызывать несколько раз для одного типа сущности, но функционально эквивалентны вызову Entity<>()
несколько раз.
Обязательный один к одному без каскадного удаления
// 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
}
По соглашению необходимые связи настраиваются для каскадного удаления. Это связано с тем, что зависимость не может существовать в базе данных после удаления субъекта. База данных может быть настроена для создания ошибки, обычно сбой приложения, а не автоматического удаления зависимых строк, которые больше не могут существовать. Для этого требуется некоторая конфигурация:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasOne(e => e.Header)
.WithOne(e => e.Blog)
.OnDelete(DeleteBehavior.Restrict);
}
Самосознание ссылки на один к одному
Во всех предыдущих примерах тип основной сущности отличается от типа зависимой сущности. Это не обязательно должно быть так. Например, в приведенных ниже типах каждый из них Person
при необходимости связан с другим 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
}
Эта связь обнаруживается по соглашению. В случаях, когда навигации, внешний ключ или обязательный или необязательный характер связи не обнаруживаются по соглашению, эти вещи можно настроить явно. Рассмотрим пример.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasOne(e => e.Husband)
.WithOne(e => e.Wife)
.HasForeignKey<Person>(e => e.HusbandId)
.IsRequired(false);
}
Примечание.
Для связей "один к одному", так как основные и зависимые типы сущностей совпадают, указывая, какой тип содержит внешний ключ, не уточняет зависимый конец. В этом случае навигация, указанная в HasOne
точках от зависимого к субъекту, и навигация, указанная в WithOne
точках от субъекта к зависимой.