¿Qué significa el fin principal de una asociación en una relación 1: 1 en el marco de la entidad?

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

Intenté hacer esto en Entity Framework cuando recibí el error:

No se puede determinar el final principal de una asociación entre los tipos 'ConsoleApplication5.Boo' y 'ConsoleApplication5.Foo'. El final principal de esta asociación debe configurarse explícitamente utilizando la API fluida de relación o las anotaciones de datos.

He visto preguntas sobre StackOverflow con una solución para este error, pero quiero entender qué significa el término "fin principal".

taher chhabrawala
fuente
Consulte docs.microsoft.com/en-us/ef/core/modeling/relationships para obtener una explicación de los términos
DeepSpace101

Respuestas:

378

En una relación uno a uno, un extremo debe ser principal y el segundo extremo debe ser dependiente. El extremo principal es el que se insertará primero y que puede existir sin el dependiente. El final dependiente es el que debe insertarse después del principal porque tiene una clave foránea para el principal.

En el caso del marco de la entidad, FK en dependiente también debe ser su PK, por lo que en su caso debe usar:

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

O mapeo fluido

modelBuilder.Entity<Foo>()
            .HasOptional(f => f.Boo)
            .WithRequired(s => s.Foo);
Ladislav Mrnka
fuente
66
@Ladislav, necesito hacer dos tablas independientes que tengan una referencia opcional entre sí (una a una), quiero que ambas tengan sus propias PK cada una, ¿cómo es esto posible? Publiqué una pregunta por separado .
Shimmy Weitzhandler
10
No tiene idea de cuántas horas tardó en encontrar una respuesta a esto: la documentación de ms es POOOOOOP ty.
gangelo
1
Tenga en cuenta que puede necesitar agregar usando System.ComponentModel.DataAnnotations.Schema; obtener ForeignKey en VS2012
stuartdotnet
2
¿Eso significa que Fooes el director entonces?
bflemi3
8
@ bflemi3 tiene razón, Booes dependiente, requiere a Fooy obtiene la clave foránea. Fooes el principal y puede existir sin a Boo.
Colin
182

También puede usar el [Required]atributo de anotación de datos para resolver esto:

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

Foose requiere para Boo.

Leniel Maccaferri
fuente
Esto era correcto para mi siguiente código donde quería un mapa entre los dos como una entidad separada de clase pública Organización {public int Id {get; conjunto; }} usuario de clase pública {public int Id {get; conjunto; }} public class UserGroup {[Clave] public int Id {get; conjunto; } [Obligatorio] público virtual Organización Organización {get; conjunto; } [Obligatorio] Usuario virtual público Usuario {get; conjunto; }}
AndyM
Estoy usando Oracle y ninguna de las API fluidas me funcionó. Gracias hermano. Tan sencillo.
CameronP
11
Tenga en cuenta que cuando utilice esta solución obtendrá excepciones de validación cuando intente actualizar una Booque acaba de recuperar de la base de datos, a menos que primero active la carga diferida de la Foopropiedad. entityframework.codeplex.com/SourceControl/network/forks/…
NathanAldenSr
2
no debería Boo Booser virtual entonces?
Simon_Weaver
1
@NathanAldenSr el enlace es malo ahora, ¿cómo haces ese cambio?
CamHart
9

Esto se refiere a la respuesta de @Ladislav Mrnka sobre el uso de una API fluida para configurar la relación uno a uno.

Tuve una situación donde tener FK of dependent must be it's PK no era factible tener.

Por ejemplo, Fooya tiene una relación de uno a muchos 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;
}

Ahora, tuvimos que agregar otra relación uno a uno entre Foo y 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;
}

Aquí se explica cómo especificar una relación uno a uno utilizando una API fluida:

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

Tenga en cuenta que si bien la adición PrimaryBarIddebe eliminarse, ya que lo especificamos a través de una API fluida.

También tenga en cuenta que el nombre del método [WithOptionalPrincipal()][1]es algo irónico. En este caso, el director es Bar. La descripción de WithOptionalDependent () en msdn lo hace más claro.

Sudarshan_SMD
fuente
2
¿Qué pasa si realmente quieres la PrimaryBarIdpropiedad? Esto es ridículo para mí. Si agrego la propiedad y digo que es la clave externa, aparece un error. Pero si no tengo la propiedad, EF la creará de todos modos. ¿Cual es la diferencia?
Chris Pratt
1
@ChrisPratt Esto puede no parecer razonable. Llegué a estas soluciones después del rastro y el error. No pude configurar el mapeo uno a uno cuando tenía PrimayBarIdpropiedad en la Fooentidad. Probablemente la misma solución que probaste. ¿Limitaciones en EF quizás?
Sudarshan_SMD
3
Sí lo es. Descubrí que EF hasta el día de hoy nunca ha implementado índices únicos. Como resultado, la única forma disponible para mapear un uno a uno es usar la clave primaria del extremo principal como la clave primaria del extremo dependiente, porque una clave primaria es única por naturaleza. En otras palabras, lo implementaron a medias y tomaron un atajo que dicta que sus tablas deben diseñarse de manera no estándar.
Chris Pratt