Co oznacza główny koniec powiązania w relacji 1: 1 w ramach jednostki

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;}
}

Próbowałem to zrobić w Entity Framework, gdy otrzymałem błąd:

Nie można określić głównego końca powiązania między typami „ConsoleApplication5.Boo” i „ConsoleApplication5.Foo”. Główny koniec tego powiązania musi zostać jawnie skonfigurowany przy użyciu interfejsu API płynnego dla relacji lub adnotacji danych.

Widziałem pytania dotyczące StackOverflow z rozwiązaniem tego błędu, ale chcę zrozumieć, co oznacza termin „główny koniec”.

taher chhabrawala
źródło
Aby uzyskać wyjaśnienie terminów,
zobacz docs.microsoft.com/en-us/ef/core/modeling/relationships

Odpowiedzi:

378

W relacji jeden do jednego jeden koniec musi być główny, a drugi koniec musi być zależny. Główny koniec to ten, który zostanie wstawiony jako pierwszy i który może istnieć bez zależnego. Koniec zależny to taki, który należy wstawić za zleceniodawcą, ponieważ ma on klucz obcy do zleceniodawcy.

W przypadku struktury jednostki FK zależna musi być również jej PK, więc w twoim przypadku powinieneś użyć:

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

Lub płynne mapowanie

modelBuilder.Entity<Foo>()
            .HasOptional(f => f.Boo)
            .WithRequired(s => s.Foo);
Ladislav Mrnka
źródło
6
@Ladislav, muszę utworzyć dwie niezależne tabele, które mają opcjonalne odniesienia do siebie (jeden do jednego), chcę, aby obie miały własne PK, jak to możliwe? Zadałem osobne pytanie .
Shimmy Weitzhandler,
10
Nie masz pojęcia, ile godzin zajęło znalezienie odpowiedzi na to pytanie - dokumentacja ms to POOOOOOP.
gangelo
1
Uwaga: może być konieczne dodanie przy użyciu System.ComponentModel.DataAnnotations.Schema; zdobyć ForeignKey w VS2012
stuartdotnet
2
Czy to oznacza, że ​​w takim razie Foojest to podstawa?
bflemi3,
8
@ bflemi3 masz rację Boojest zależny, wymaga a Fooi dostaje klucz obcy. Foojest głównym i może istnieć bez Boo.
Colin,
182

[Required]Aby rozwiązać ten problem, możesz również użyć atrybutu adnotacji danych:

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; }
}

Foojest wymagane dla Boo.

Leniel Maccaferri
źródło
Było to poprawne w moim poniższym kodzie, w którym chciałem mapę między nimi jako oddzielną jednostką public class Organization {public int Id {get; zestaw; }} użytkownik klasy publicznej {public int Id {get; zestaw; }} public class UserGroup {[Key] public int Id {get; zestaw; } [Wymagane] publiczna wirtualna organizacja organizacji {get; zestaw; } [Wymagany] publiczny wirtualny użytkownik Użytkownik {get; zestaw; }}
AndyM
Używam Oracle i żaden z płynnych API nie działał dla mnie. Dzięki stary. Tak prosty.
CameronP,
11
Należy pamiętać, że podczas korzystania z tego rozwiązania otrzymasz wyjątki sprawdzania poprawności, gdy spróbujesz zaktualizować plik Boowłaśnie pobrany z bazy danych, chyba że najpierw uruchomisz leniwe ładowanie Foowłaściwości. entityframework.codeplex.com/SourceControl/network/forks/…
NathanAldenSr
2
nie powinien Boo Boobyć więc wirtualny?
Simon_Weaver
1
@NathanAldenSr link jest teraz zły, jak wprowadzić tę zmianę?
CamHart,
9

Odnosi się to do odpowiedzi @Ladislav Mrnka na używanie płynnego interfejsu API do konfigurowania relacji jeden do jednego.

Miałem sytuację, w której posiadanie FK of dependent must be it's PKbyło niemożliwe.

Np. FooMa już relację jeden do wielu 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;
}

Teraz musieliśmy dodać kolejną relację jeden do jednego między Foo i Barem.

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;
}

Oto jak określić relację jeden do jednego za pomocą płynnego interfejsu API:

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

Pamiętaj, że podczas dodawania PrimaryBarIdnależy usunąć, ponieważ określamy go za pomocą płynnego interfejsu API.

Zauważ też, że nazwa metody [WithOptionalPrincipal()][1]jest trochę ironiczna. W tym przypadku Zleceniodawcą jest Bar. Opis WithOptionalDependent () na msdn czyni go bardziej zrozumiałym.

Sudarshan_SMD
źródło
2
Co jeśli naprawdę chceszPrimaryBarIdnieruchomość? To jest dla mnie śmieszne. Jeśli dodam właściwość i stwierdzę, że jest to klucz obcy, pojawia się błąd. Ale jeśli nie mam tej właściwości, EF i tak ją utworzy. Co za różnica?
Chris Pratt
1
@ChrisPratt To może nie brzmieć rozsądnie. Do tych rozwiązań dotarłem po śladach i błędach. Nie miałem możliwości skonfigurowania odwzorowania jeden do jednego, gdy miałem PrimayBarIdwłaściwość w obiekcie Foo. Prawdopodobnie to samo rozwiązanie, które wypróbowałeś. Być może ograniczenia w EF?
Sudarshan_SMD
3
Tak jest. Przekonałem się, że EF do dziś nigdy nie wdrożył unikalnych indeksów. W rezultacie jedynym dostępnym sposobem odwzorowania jeden do jednego jest użycie klucza podstawowego głównego końca jako klucza podstawowego zależnego końca, ponieważ klucz podstawowy jest z natury unikalny. Innymi słowy, wdrożyli go w połowie i przyjęli skrót, który nakazuje, że stoły muszą być zaprojektowane w niestandardowy sposób.
Chris Pratt