2012-06-02 19 views
6

Esto debería ser una pregunta simple para el usuario de EF versado.Asignación de muchas a muchas relaciones con referencia de clave externa

Tengo el siguiente esquema (en mi cabeza) de cómo deberían verse las relaciones entre las tablas.

[FooBar]  [Foo]   [Bar] 

FooId PK,FK Id PK   Id PK 
BarId PK,FK BarId FK  Name 
IsRead  Name   Description 
       Description  

Sin embargo, cuando intento generar el esquema utilizando EF-código primero se produce un error de interpretar las relaciones entre las entidades como los he interpreté (agrega clave externa FooId a la mesa [bar]) y falla para crear completamente la tabla de puente [FooBar].

Si alguien pudiera guiarme sobre cómo lograr el esquema anterior utilizando el código EF4, lo agradecería. Si la solución implica atributos en mis modelos POCO, las configuraciones fluidas o un híbrido de ambos no importa mucho, siempre que se cree el esquema de base de datos deseado.

Modelos

Poco:

public class Foo 
{ 
    public int Id { get; set; } 
    public string Text { get; set; } 
    public string Description { get; set; } 
    public int BarId { get; set; } 

    public Bar Bar { get; set; } /* bar entity */ 

    public virtual ICollection<Bar> BridgedBars { get; set; } 

    public Foo() 
    { 
     Bars = new List<Bar>(); 
    } 
} 

public class Bar 
{ 
    public int Id { get; set; } 
    public string Text { get; set; } 
    public string Description { get; set; } 

    public virtual ICollection<Foo> Foos { get; set; } 
    public virtual ICollection<Foo> BridgedFoos { get; set; } 

    public Bar() 
    { 
     Foos = new List<Foo>(); 
     BridgedFoos = new List<Foo>(); 
    } 
} 

public class FooBar 
{ 
    public int FooId { get; set; } 
    public int BarId { get; set; } 

    public virtual Foo Foo { get; set; } 
    public virtual Bar Bar { get; set; } 

    public bool IsRead { get; set; } 
} 
+0

Como tener una propiedad (IsRead) en la relación FooBar que probablemente desee establecer desde el código, tanto Foo como Bar necesitan que sus colecciones relacionadas sean FooBars. Una vez que mapea las propiedades en ambos extremos (es decir, las colecciones virtuales en Foo y Bar) a FooBar EF debe crear la relación correctamente. Utilice HasMany(). WithRequired() para asignar cada clase a FooBar. –

Respuesta

8

Su modelo será de hecho crear una clave externa FooId en el Bar que pertenece a la relación definida por Foo.BrideBars. EF no relaciona esta propiedad de navegación con una de las propiedades ICollection<Foo> en Bar porque hay dos y EF no puede determinar de manera única cuál es el par correcto. Como resultado, crea una relación para Foo.BrideBars sin una propiedad de navegación en el otro extremo. Por así decirlo, hay una propiedad Bar.Foo invisible que causa la clave externa.

El esquema de base de datos que desea asignar a un modelo realmente no representa una relación muchos a muchos, sino dos relaciones uno a muchos con la entidad intermedia "puente" FooBar. Debe usar esta clase en las propiedades de navegación para definir las relaciones correctas. Se vería así:

public class Foo 
{ 
    public int Id { get; set; } 
    public string Text { get; set; } 
    public string Description { get; set; } 

    public int BarId { get; set; } 
    public Bar Bar { get; set; } 

    public virtual ICollection<FooBar> FooBars { get; set; } 
} 

public class Bar 
{ 
    public int Id { get; set; } 
    public string Text { get; set; } 
    public string Description { get; set; } 

    public virtual ICollection<Foo> Foos { get; set; } 
    public virtual ICollection<FooBar> FooBars { get; set; } 

} 

public class FooBar 
{ 
    [Key, Column(Order = 0)] 
    public int FooId { get; set; } 
    [Key, Column(Order = 1)] 
    public int BarId { get; set; } 

    public virtual Foo Foo { get; set; } 
    public virtual Bar Bar { get; set; } 

    public bool IsRead { get; set; } 
} 

Las relaciones correctas se detectarán mediante convenciones de nomenclatura en este modelo. Solo para la entidad FooBar es necesario definir una clave explícitamente porque los nombres de las propiedades no cumplen con las convenciones (no Id y ninguna propiedad de FooBarId). En este modelo, tiene sentido utilizar una clave compuesta en FooBar.

Supongo que sus clases y propiedades reales no tienen el nombre Foo y Bar. Si sus nombres reales no siguen las convenciones que posiblemente tenga que especificar las relaciones con anotaciones - o con la API de Fluido:

modelBuilder.Entity<Foo>() 
    .HasRequired(f => f.Bar) 
    .WithMany(b => b.Foos) 
    .HasForeignKey(f => f.BarId); 

modelBuilder.Entity<FooBar>() 
    .HasKey(fb => new { fb.FooId, fb.BarId }); // replaces the [Key] annotations 

modelBuilder.Entity<FooBar>() 
    .HasRequired(fb => fb.Foo) 
    .WithMany(f => f.FooBars) 
    .HasForeignKey(fb => fb.FooId); 

modelBuilder.Entity<FooBar>() 
    .HasRequired(fb => fb.Bar) 
    .WithMany(b => b.FooBars) 
    .HasForeignKey(fb => fb.BarId); 

En el esquema de base de datos de la tabla FooBar tendrá una clave principal compuesta:

[FooBar]  [Foo]   [Bar] 

FooId PK,FK Id PK   Id PK 
BarId PK,FK BarId FK  Name 
IsRead   Name   Description 
       Description  

Pero es necesario tener un PK en FooBar porque cada entidad en un modelo EF debe tener una propiedad clave definida, ya sea simple o compuesta, que se asigna a una clave primaria en la tabla de la base de datos.

En esta pregunta - Create code first, many to many, with additional fields in association table - hay más detalles sobre cómo trabajar con ese tipo de relación. (A veces, las personas también lo llaman "muchos-a-muchos relación con carga útil" (la propiedad IsRead es la "carga útil" en su modelo de ejemplo), pero en realidad no es de muchos a muchos.)

+0

exactamente lo que estaba buscando y sí tenía una clave primaria compuesta en 'FooBar' era mi intención, ¡simplemente me olvidé de mencionarlo! ¡Gracias por la gran respuesta y explicación! una vez que mi perfil logre * 15 puntos * seguramente volveré y votaré. – culturalanomoly

Cuestiones relacionadas