Что означает основной конец ассоциации в отношении 1: 1 в структуре Entity

269
public class Foo
{
    public string FooId{get;set;}
    public Boo Boo{get;set;}
}


public class Boo
{
    public string BooId{get;set;}
    public Foo Foo{get;set;}
}

Я пытался сделать это в Entity Framework, когда я получил ошибку:

Невозможно определить основной конец ассоциации между типами «ConsoleApplication5.Boo» и «ConsoleApplication5.Foo». Основной конец этой ассоциации должен быть явно сконфигурирован с использованием API свободного взаимодействия или аннотаций данных.

Я видел вопросы о StackOverflow с решением этой ошибки, но я хочу понять, что означает термин «основной конец».

Тахер Чабравала
источник
См. Docs.microsoft.com/en-us/ef/core/modeling/relationships для объяснения терминов
DeepSpace101

Ответы:

378

В отношении «один к одному» один конец должен быть основным, а второй конец должен быть зависимым. Основной конец - это тот, который будет вставлен первым и может существовать без зависимого. Зависимый конец - тот, который должен быть вставлен после принципала, потому что у него есть внешний ключ к принципалу.

В случае фреймворка сущности FK также должен быть его PK, поэтому в вашем случае вы должны использовать:

public class Boo
{
    [Key, ForeignKey("Foo")]
    public string BooId{get;set;}
    public Foo Foo{get;set;}
}

Или беглое отображение

modelBuilder.Entity<Foo>()
            .HasOptional(f => f.Boo)
            .WithRequired(s => s.Foo);
Ладислав Мрнка
источник
6
@Ladislav, мне нужно сделать две независимые таблицы, каждая из которых имеет необязательную ссылку друг на друга (один к одному), я хочу, чтобы у них обоих были свои собственные PK, как это возможно? Я разместил отдельный вопрос .
Шимми Вайцхандлер,
10
Вы не представляете, сколько часов потребовалось, чтобы найти ответ на этот вопрос - MS документация POOOOOOP.
Гангело
1
Обратите внимание, что вам может потребоваться добавить с помощью System.ComponentModel.DataAnnotations.Schema; получить ForeignKey в VS2012
stuartdotnet
2
Значит ли это, что Fooэто основной?
bflemi3
8
@ bflemi3 вы правы, Booзависимый, требует a Fooи получает внешний ключ. Fooявляется основным и может существовать без Boo.
Колин
182

Вы также можете использовать [Required]атрибут аннотации данных, чтобы решить это:

public class Foo
{
    public string FooId { get; set; }

    public Boo Boo { get; set; }
}

public class Boo
{
    public string BooId { get; set; }

    [Required]
    public Foo Foo {get; set; }
}

Fooтребуется для Boo.

Лениэль Маккаферри
источник
Это было правильно для моего следующего кода, где я хотел отобразить карту между ними как отдельный объект public class Organization {public int Id {get; устанавливать; }} открытый класс user {public int Id {get; устанавливать; }} открытый класс UserGroup {[Key] public int Id {get; устанавливать; } [Обязательно] публичная виртуальная организация Organization {get; устанавливать; } [Обязательно] публичный виртуальный пользователь Пользователь {get; устанавливать; }}
AndyM
Я использую Oracle, и ни один из свободно используемых API не работает для меня. Спасибо брат. Так просто.
CameronP
11
Имейте в виду, что при использовании этого решения вы получите исключения проверки при попытке обновить только Booчто извлеченную из базы данных информацию, если только вы сначала не запустите отложенную загрузку Fooсвойства. entityframework.codeplex.com/SourceControl/network/forks/…
NathanAldenSr
2
не должно Boo Booбыть виртуальным тогда?
Simon_Weaver
1
@NathanAldenSr ссылка сейчас плохая, как ты можешь это изменить?
CamHart
9

Это со ссылкой на ответ @Ladislav Mrnka об использовании свободного API для настройки отношений один-к-одному.

Была ситуация, когда не FK of dependent must be it's PKбыло возможности.

Например, Fooуже имеет отношения один-ко-многим Bar.

public class Foo {
   public Guid FooId;
   public virtual ICollection<> Bars; 
}
public class Bar {
   //PK
   public Guid BarId;
   //FK to Foo
   public Guid FooId;
   public virtual Foo Foo;
}

Теперь нам пришлось добавить еще одно отношение один к одному между Фу и Баром.

public class Foo {
   public Guid FooId;
   public Guid PrimaryBarId;// needs to be removed(from entity),as we specify it in fluent api
   public virtual Bar PrimaryBar;
   public virtual ICollection<> Bars;
}
public class Bar {
   public Guid BarId;
   public Guid FooId;
   public virtual Foo PrimaryBarOfFoo;
   public virtual Foo Foo;
}

Вот как определить отношение один-к-одному, используя свободный API:

modelBuilder.Entity<Bar>()
            .HasOptional(p => p.PrimaryBarOfFoo)
            .WithOptionalPrincipal(o => o.PrimaryBar)
            .Map(x => x.MapKey("PrimaryBarId"));

Обратите внимание, что при добавлении PrimaryBarIdнеобходимо удалить, так как мы указываем его через свободный API.

Также обратите внимание, что название метода [WithOptionalPrincipal()][1]довольно иронично. В этом случае Принципал - Бар. Описание WithOptionalDependent () в msdn делает его более понятным.

Sudarshan_SMD
источник
2
Что делать , если вы на самом деле хотите в PrimaryBarIdсобственность? Это смешно для меня. Если я добавлю свойство и скажу, что это внешний ключ, я получу ошибку. Но если у меня нет собственности, то EF все равно ее создаст. Какая разница?
Крис Пратт
1
@ChrisPratt Это может не звучать разумно. Я пришел к этому решению после следа и ошибки. Не удалось настроить сопоставление один-к-одному, когда у меня было PrimayBarIdсвойство в Fooобъекте. Скорее всего, то же решение, которое вы пробовали. Ограничения в EF возможно?
Sudarshan_SMD
3
Да, так и есть. Я пришел к выводу, что EF до сих пор никогда не внедряла уникальные индексы. В результате единственный доступный способ сопоставления один-к-одному состоит в использовании первичного ключа основного конца в качестве первичного ключа зависимого конца, поскольку первичный ключ по своей природе уникален. Другими словами, они наполовину реализовали это и воспользовались ярлыком, который требует, чтобы ваши таблицы разрабатывались нестандартным способом.
Крис Пратт