Buscar..


Introducción

El tema trata sobre cómo puede mapear relaciones de uno a muchos y de muchos a muchos usando el Código Entity Framework Primero.

Mapeo uno a muchos

Así que digamos que tienes dos entidades diferentes, algo como esto:

public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
}

public class MyDemoContext : DbContext
{
   public DbSet<Person> People { get; set; }
   public DbSet<Car> Cars { get; set; }
}

Y desea configurar una relación de uno a varios entre ellos, es decir, una persona puede tener cero, uno o más automóviles, y un automóvil pertenece a una sola persona exactamente. Toda relación es bidireccional, por lo que si una persona tiene un automóvil, el automóvil pertenece a esa persona.

Para hacer esto solo modifica tus clases modelo:

public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<Car> Cars { get; set; } // don't forget to initialize (use HashSet)
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
   public int PersonId { get; set; }
   public virtual Person Person { get; set; }
}

Y eso es :) Ya tienes tu relación configurada. En la base de datos, esto se representa con claves externas, por supuesto.

Mapeo de uno a muchos: contra la convención

En el último ejemplo, puede ver que EF determina qué columna es la clave externa y hacia dónde debe apuntar. ¿Cómo? Mediante el uso de convenciones. Tener una propiedad de tipo Person que se denomina Person con una propiedad PersonId lleva a EF a concluir que PersonId es una clave externa y apunta a la clave principal de la tabla representada por el tipo Person .

Pero, ¿qué sucedería si cambiara PersonId a OwnerId y Person a Owner en el tipo de automóvil ?

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }
   public int OwnerId { get; set; }
   public virtual Person Owner { get; set; }
}

Bueno, desafortunadamente en este caso, las convenciones no son suficientes para producir el esquema de base de datos correcto:

Sin preocupaciones; puede ayudar a EF con algunos consejos sobre sus relaciones y claves en el modelo. Simplemente configure su tipo de Car para usar la propiedad OwnerId como FK. Cree una configuración de tipo de entidad y aplíquela en su OnModelCreating() :

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasRequired(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
  }
}

Básicamente, esto dice que el Car tiene una propiedad requerida, el Owner ( HasRequired () ) y en el tipo de Owner , la propiedad Cars se usa para referirse a las entidades del automóvil ( WithMany () ). Y finalmente se especifica la propiedad que representa la clave externa ( HasForeignKey () ). Esto nos da el esquema que queremos:

También puedes configurar la relación desde el lado de la Person :

public class PersonEntityTypeConfiguration : EntityTypeConfiguration<Person>
{
  public PersonEntityTypeConfiguration()
  {
    this.HasMany(p => p.Cars).WithRequired(c => c.Owner).HasForeignKey(c => c.OwnerId);
  }
}

La idea es la misma, solo los lados son diferentes (tenga en cuenta cómo puede leerlo todo: 'esta persona tiene muchos autos, cada uno con un propietario requerido'). No importa si configura la relación desde el lado Person o el lado Car . Incluso puede incluir ambos, pero en este caso, ¡tenga cuidado de especificar la misma relación en ambos lados!

Mapeo cero o uno a muchos

En los ejemplos anteriores, un coche no puede existir sin una persona. ¿Qué pasaría si quisiera que la persona fuera opcional desde el lado del automóvil? Bueno, es algo fácil, saber cómo hacer uno a muchos. Solo cambia el PersonId en Car para que sea nullable:

public class Car
{
    public int CarId { get; set; }
    public string LicensePlate { get; set; }
    public int? PersonId { get; set; }
    public virtual Person Person { get; set; }
}

Y luego use HasOptional () (o WithOptional () , dependiendo de qué lado hace la configuración):

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
  public CarEntityTypeConfiguration()
  {
     this.HasOptional(c => c.Owner).WithMany(p => p.Cars).HasForeignKey(c => c.OwnerId);
  }
}

Muchos a muchos

Vayamos al otro escenario, donde cada persona puede tener múltiples autos y cada automóvil puede tener múltiples dueños (pero, nuevamente, la relación es bidireccional). Esta es una relación de muchos a muchos. La forma más fácil es dejar que EF haga su magia usando convenciones.

Solo cambia el modelo de esta manera:

 public class Person
{
   public int PersonId { get; set; }
   public string Name { get; set; }
   public virtual ICollection<Car> Cars { get; set; }
}

public class Car
{
   public int CarId { get; set; }
   public string LicensePlate { get; set; }        
   public virtual ICollection<Person> Owners { get; set; }
}

Y el esquema:

Casi perfecto. Como puede ver, EF reconoció la necesidad de una tabla de unión, donde pueda realizar un seguimiento de los emparejamientos persona-automóvil.

Muchos a muchos: personalizando la tabla de unión

Es posible que desee cambiar el nombre de los campos en la tabla de unión para que sea un poco más amigable. Puede hacerlo utilizando los métodos de configuración habituales (de nuevo, no importa de qué lado realice la configuración):

public class CarEntityTypeConfiguration : EntityTypeConfiguration<Car>
{
   public CarEntityTypeConfiguration()
   {
      this.HasMany(c => c.Owners).WithMany(p => p.Cars)
          .Map(m =>
              {
                 m.MapLeftKey("OwnerId");
                 m.MapRightKey("CarId");
                 m.ToTable("PersonCars");
              }
        );
  }
}

Incluso muy fácil de leer: este automóvil tiene muchos propietarios ( HasMany () ), y cada propietario tiene muchos automóviles ( WithMany () ). Asigne esto de modo que asigne la clave izquierda a OwnerId ( MapLeftKey () ), la clave derecha a CarId ( MapRightKey () ) y todo a la tabla PersonCars ( ToTable () ). Y esto te da exactamente ese esquema:

Many-to-many: entidad de unión personalizada

Debo admitir que no soy realmente un fanático de permitir que EF infiera la tabla de unión sin una entidad de unión. No puede rastrear información adicional a una asociación persona-automóvil (digamos la fecha a partir de la cual es válida), porque no puede modificar la tabla.

Además, el CarId en la tabla de unión es parte de la clave principal, por lo que si la familia compra un auto nuevo, primero debe eliminar las asociaciones antiguas y agregar otras nuevas. EF oculta esto de usted, pero esto significa que tiene que hacer estas dos operaciones en lugar de una simple actualización (sin mencionar que las inserciones / eliminaciones frecuentes pueden llevar a la fragmentación del índice, lo que es una solución fácil para eso).

En este caso, lo que puede hacer es crear una entidad de unión que tenga una referencia tanto a un automóvil específico como a una persona específica. Básicamente, usted ve su asociación de muchos a muchos como una combinación de dos asociaciones de uno a muchos:

public class PersonToCar
{
   public int PersonToCarId { get; set; }
   public int CarId { get; set; }
   public virtual Car Car { get; set; }
   public int PersonId { get; set; }
   public virtual Person Person { get; set; }
   public DateTime ValidFrom { get; set; }
}

public class Person
{
  public int PersonId { get; set; }
  public string Name { get; set; }
  public virtual ICollection<PersonToCar> CarOwnerShips { get; set; }
}

public class Car
{
  public int CarId { get; set; }
  public string LicensePlate { get; set; }        
  public virtual ICollection<PersonToCar> Ownerships { get; set; }
}

public class MyDemoContext : DbContext
{
  public DbSet<Person> People { get; set; }
  public DbSet<Car> Cars { get; set; }
  public DbSet<PersonToCar> PersonToCars { get; set; }
}

Esto me da mucho más control y es mucho más flexible. Ahora puedo agregar datos personalizados a la asociación y cada asociación tiene su propia clave principal, por lo que puedo actualizar el auto o la referencia del propietario en ellos.

Tenga en cuenta que esto es solo una combinación de dos relaciones de uno a varios, por lo que puede usar todas las opciones de configuración que se analizaron en los ejemplos anteriores.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow