O que significa o fim principal de uma associação no relacionamento 1: 1 na estrutura da Entidade

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

Eu estava tentando fazer isso no Entity Framework quando recebi o erro:

Não foi possível determinar o fim principal de uma associação entre os tipos 'ConsoleApplication5.Boo' e 'ConsoleApplication5.Foo'. A extremidade principal desta associação deve ser explicitamente configurada usando a API fluente do relacionamento ou anotações de dados.

Vi perguntas no StackOverflow com uma solução para esse erro, mas quero entender o significado do termo "fim principal".

taher chhabrawala
fonte
Consulte docs.microsoft.com/en-us/ef/core/modeling/relationships para obter uma explicação dos termos
DeepSpace101 19/10/19

Respostas:

378

Na relação um para um, um extremo deve ser principal e o segundo extremo deve ser dependente. Extremidade principal é aquela que será inserida primeiro e que pode existir sem a dependente. Extremidade dependente é aquele que deve ser inserido após o principal, porque possui chave estrangeira para o principal.

No caso da estrutura da entidade, o FK em dependente também deve ser o seu PK; portanto, no seu caso, você deve usar:

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

Ou mapeamento fluente

modelBuilder.Entity<Foo>()
            .HasOptional(f => f.Boo)
            .WithRequired(s => s.Foo);
Ladislav Mrnka
fonte
6
@Ladislav, preciso criar duas tabelas independentes que tenham uma referência opcional uma à outra (uma a uma), quero que ambas tenham suas próprias PKs, como isso é possível? Eu postei uma pergunta separada .
Shimmy Weitzhandler
10
Você não tem idéia de quantas horas foram necessárias para encontrar uma resposta a essa documentação - ms é POOOOOOP ty.
gangelo
1
Observe que você pode precisar adicionar using System.ComponentModel.DataAnnotations.Schema; obter ForeignKey no VS2012
stuartdotnet
2
Isso significa que esse Fooé o principal então?
bflemi3
8
@ bflemi3 você está correto Booé o dependente, requer ae Fooobtém a chave estrangeira. Fooé o principal e pode existir sem a Boo.
Colin
182

Você também pode usar o [Required]atributo de anotação de dados para resolver isso:

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é necessário para Boo.

Leniel Maccaferri
fonte
Isso estava correto para o meu código a seguir, onde eu queria um mapa entre os dois como uma entidade pública de classe separada Organization {public int Id {get; conjunto; }} usuário de classe pública {public int Id {get; conjunto; }} public class UserGroup {[Key] public int Id {get; conjunto; } Organização pública pública obrigatória [get; conjunto; } Usuário público público obrigatório [get; conjunto; }}
AndyM
Estou usando o Oracle e nenhuma das APIs fluentes funcionou para mim. Obrigado mano. Tão simples.
precisa saber é o seguinte
11
Esteja ciente de que, ao usar esta solução, você obterá exceções de validação ao tentar atualizar uma Booque você acabou de recuperar do banco de dados, a menos que você inicie o carregamento lento da Foopropriedade. entityframework.codeplex.com/SourceControl/network/forks/…
NathanAldenSr
2
Boo Booentão não deveria ser virtual?
Simon_Weaver
1
@NathanAldenSr o ​​link está ruim agora, como você faz essa alteração?
CamHart:
9

Isso se refere à resposta de @Ladislav Mrnka sobre o uso de API fluente para configurar o relacionamento individual.

Teve uma situação em que ter FK of dependent must be it's PKnão era viável.

Por exemplo, Foojá possui um relacionamento com muitos 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;
}

Agora, tivemos que adicionar outro relacionamento individual entre Foo e Bar.

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

Aqui está como especificar o relacionamento individual usando a API fluente:

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

Observe que a adição PrimaryBarIdprecisa ser removida, pois a especificamos por meio de API fluente.

Observe também que o nome do método [WithOptionalPrincipal()][1]é meio irônico. Nesse caso, Principal é Bar. A descrição de WithOptionalDependent () no msdn torna mais clara.

Sudarshan_SMD
fonte
2
E se você realmente quiser a PrimaryBarIdpropriedade? Isso é ridículo para mim. Se eu adicionar a propriedade e disser que é a chave estrangeira, recebo um erro. Mas se eu não tiver a propriedade, a EF a criará de qualquer maneira. Qual é a diferença?
Chris Pratt
1
@ ChrisPratt Isso pode não parecer razoável. Cheguei a estas soluções após rastro e erro. Não foi possível configurar o mapeamento um para um quando eu tinha PrimayBarIdpropriedade na Fooentidade. Provavelmente a mesma solução que você tentou. Limitações na EF, talvez?
Sudarshan_SMD
3
Sim é. Descobri que a EF até hoje nunca implementou índices únicos. Como resultado, a única maneira disponível para mapear um um para um é usar a chave primária da extremidade principal como a chave primária da extremidade dependente, porque uma chave primária é por natureza única. Em outras palavras, eles o implementaram pela metade e adotaram um atalho que determina que suas tabelas devem ser projetadas de maneira não-padrão.
Chris Pratt