Поиск…


Вступление

В этом разделе обсуждается, как вы можете сопоставить отношения «один ко многим» и «многие ко многим», используя First Entity Framework Code First.

Отображение «один ко многим»

Итак, допустим, у вас есть две разные сущности, что-то вроде этого:

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

И вы хотите настроить отношения «один ко многим» между ними, то есть один человек может иметь ноль, один или несколько автомобилей, и один автомобиль принадлежит одному человеку точно. Каждое отношение является двунаправленным, поэтому, если у человека есть автомобиль, автомобиль принадлежит этому лицу.

Для этого просто измените классы моделей:

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

И это все :) У вас уже настроены ваши отношения. Конечно, в базе данных это внешние ключи.

Сопоставление «один ко многим»: против конвенции

В последнем примере вы можете видеть, что EF определяет, какой столбец является внешним ключом и где он должен указывать. Как? Используя соглашения. Наличие свойства типа Person , которое называется Person с свойством PersonId приводит EF к выводу, что PersonId является внешним ключом и указывает первичный ключ таблицы, представленной типом Person .

Но что делать, если вы изменили PersonId на OwnerId и Person to Owner в типе автомобиля ?

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

Ну, к сожалению, в этом случае соглашений недостаточно для создания правильной схемы БД:

Не волнуйтесь; вы можете помочь EF с некоторыми подсказками о ваших отношениях и ключах в модели. Просто настройте свой тип Car чтобы использовать свойство OwnerId как FK. Создайте конфигурацию типа сущности и примените ее в свой OnModelCreating() :

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

В основном это говорит о том, что Car имеет требуемое свойство, Owner ( HasRequired () ) и в типе Owner , свойство Cars используется для возврата к автообъектам (WithMany () ). И, наконец, указывается свойство, представляющее внешний ключ ( HasForeignKey () ). Это дает нам схему, которую мы хотим:

Вы также можете настроить отношения со стороны Person :

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

Идея такая же, только стороны разные (обратите внимание, как вы можете прочитать все это: «у этого человека много автомобилей, каждый автомобиль с требуемым владельцем»). Не имеет значения, настроите ли вы отношения со стороны « Person или « Car . Вы можете даже включить оба, но в этом случае будьте осторожны, чтобы указать те же отношения с обеих сторон!

Отображение нуля или один-ко-многим

В предыдущих примерах автомобиль не может существовать без человека. Что, если вы хотите, чтобы человек был необязательным со стороны автомобиля? Ну, это легко, зная, как делать «один ко многим». Просто измените PersonId в Car на значение NULL:

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

И затем используйте HasOptional () (или WithOptional () , в зависимости от того, с какой стороны вы выполните настройку):

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

Многие-ко-многим

Перейдем к другому сценарию, где каждый человек может иметь несколько автомобилей, и каждый автомобиль может иметь несколько владельцев (но опять-таки, отношение двунаправлено). Это отношения «многие ко многим». Самый простой способ - позволить EF делать это с помощью условностей.

Просто измените модель следующим образом:

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

И схема:

Практически идеально. Как вы можете видеть, EF признал необходимость подключения таблицы, где вы можете отслеживать парные пары.

«Множество ко многим»: настройка таблицы соединений

Возможно, вы захотите переименовать поля в таблице соединений, чтобы быть немного более дружелюбными. Вы можете сделать это, используя обычные методы настройки (опять же, неважно, с какой стороны вы выполняете настройку):

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

Довольно легко читается: у этого автомобиля много владельцев ( HasMany () ), причем каждый владелец имеет много автомобилей ( WithMany () ). Сопоставьте это так, чтобы вы наложили левую клавишу на OwnerId ( MapLeftKey () ), правую клавишу на CarId ( MapRightKey () ) и все это на таблицу PersonCars ( ToTable () ). И это дает вам именно эту схему:

«Множество ко многим»: настраиваемый элемент объединения

Я должен признать, что я не являюсь поклонником позволить EF вывести таблицу соединений с объединенной сущностью. Вы не можете отслеживать дополнительную информацию в ассоциации с персоналом (допустим, дата, с которой она действительна), потому что вы не можете изменить таблицу.

Кроме того, CarId в таблице соединений является частью первичного ключа, поэтому, если семья покупает новый автомобиль, вы должны сначала удалить старые ассоциации и добавить новые. EF скрывает это от вас, но это означает, что вам нужно выполнить эти две операции вместо простого обновления (не говоря уже о том, что частые вставки / удаления могут привести к фрагментации индекса - хорошо, что для этого есть легкое решение ).

В этом случае вы можете создать объект объединения, который имеет ссылку как на один конкретный автомобиль, так и на одного конкретного человека. В основном вы смотрите на свою ассоциацию «многие-ко-многим» как комбинации двух ассоциаций «один-ко-многим»:

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

Это дает мне гораздо больший контроль, и это намного более гибко. Теперь я могу добавить пользовательские данные в ассоциацию, и каждая ассоциация имеет свой собственный первичный ключ, поэтому я могу обновить ссылку на автомобиль или владельца.

Обратите внимание, что это действительно просто комбинация двух отношений «один-ко-многим», поэтому вы можете использовать все параметры конфигурации, описанные в предыдущих примерах.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow