Entity Framework Code First Class с родителем и потомками того же типа, что и его собственный класс - PullRequest
7 голосов
/ 11 февраля 2012

У меня есть класс Content, который должен иметь родительский идентификатор для наследования, но я также хочу, чтобы у него был список дочернего контента, который не имеет ничего общего с этим деревом наследования.

Я в основном хотелтаблица ссылок как ChildContentRelationship с идентификаторами для parentContent и childContent в нем, и класс Content будет содержать список ChildContentRelationship.

Это вызвало много ошибок.

Вот что я хочуделать

public class Content
{
    public int Id { get; set; }

    public int? ParentContentId { get; set; }
    public virtual Content ParentContent { get; set; }

    public string Name { get; set; }

    public int ContentTypeId { get; set; }
    public virtual ContentType ContentType { get; set; }

    public virtual ICollection<Property> Properties { get; set; }

    public virtual ICollection<ChildContentRelationship> ChildContent { get; set; } 
}

Как бы это настроить в EF?

1 Ответ

18 голосов
/ 12 февраля 2012

Я не уверен, правильно ли я понимаю вашу модель.Давайте обсудим варианты.

На мгновение я опускаю эту дополнительную сущность ChildContentRelationship и предполагаю, что коллекция ChildContent имеет тип ICollection<Content>.

  • Option1:

    Я предполагаю, что ParentContent является обратным свойством из ChildContent.Это означало бы, что если у вас есть Content с Id = x, а у этого Содержимого есть ChildContent с Id = y, то ChildContents ParentContentId всегда должен быть x.Это будет только одна ассоциация, а ParentContent и ChildContent являются конечными точками этой же ассоциации.

    Отображение для этой связи может быть создано с помощью аннотаций данных ...

    [InverseProperty("ParentContent")]
    public virtual ICollection<Content> ChildContent { get; set; }
    

    ... или с Fluent API:

    modelBuilder.Entity<Content>()
        .HasOptional(c => c.ParentContent)
        .WithMany(c => c.ChildContent)
        .HasForeignKey(c => c.ParentContentId);
    

    Я думаю, это не то, что вы хотите ( "... не имеет ничего общего с ..." ).Попробуйте переименовать ваши свойства навигации, хотя.Если кто-то читает Parent... и Child..., он, скорее всего, предположит, что он построил пару навигационных свойств для одного и того же отношения.

  • Вариант 2:

    ParentContent не является обратным свойством ChildContent, что означает, что у вас на самом деле есть два независимых отношения, а вторая конечная точка обоих отношений не отображается в вашем классе модели.

    Отображение для ParentContent будет выглядеть следующим образомэто:

    modelBuilder.Entity<Content>()
        .HasOptional(c => c.ParentContent)
        .WithMany()
        .HasForeignKey(c => c.ParentContentId);
    

    WithMany() без параметров означает, что вторая конечная точка не является свойством в классе вашей модели, особенно это не ChildContent.

    Теперь остается вопрос: к каким отношениям относится ChildContent?Это отношение «один ко многим» или отношение «многие ко многим»?

    • Вариант 2a

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

      Отображение для ChildContent будет выглядеть следующим образом:

      modelBuilder.Entity<Content>()
          .HasMany(c => c.ChildContent)
          .WithOptional(); // or WithRequired()
      

      У вас будет дополнительный столбец внешнего ключа в таблице Content в вашей базе данных, который принадлежит этой ассоциации, но не имеет соответствующего свойства FK в классе сущности.

    • Вариант 2b

      Если многие Content s могут относиться к одним и тем же ChildContent s, то у вас есть отношение многие ко многим.(Это похоже на отношения между пользователем и ролями: в одной роли может быть много пользователей, и у пользователя может быть много ролей.)

      Отображение для ChildContent будет выглядеть так:

      modelBuilder.Entity<Content>()
          .HasMany(c => c.ChildContent)
          .WithMany()
          .Map(x =>
          {
              x.MapLeftKey("ParentId");
              x.MapRightKey("ChildId");
              x.ToTable("ChildContentRelationships");
          });
      

      Это сопоставление создаст таблицу объединения ChildContentRelationships в базе данных, но вам не нужен соответствующий объект для этой таблицы.

    • Опция 2c

      Только в том случае, если отношение «многие ко многим» имеет больше свойств в дополнение к двум ключам (ParentId и ChildId) (например, что-то вроде CreationDate или RelationshipType или ...)вам нужно будет ввести новую сущность ChildContentRelationship в вашу модель:

      public class ChildContentRelationship
      {
          [Key, Column(Order = 0)]
          public int ParentId { get; set; }
          [Key, Column(Order = 1)]
          public int ChildId { get; set; }
      
          public Content Parent { get; set; }
          public Content Child { get; set; }
      
          public DateTime CreationDate { get; set; }
          public string RelationshipType { get; set; }
      }
      

      Теперь ваш класс Content будет иметь коллекцию ChildContentRelationship s:

      public virtual ICollection<ChildContentRelationship> ChildContent
          { get; set; }
      

      Иу вас есть два отношения один ко многим:

      modelBuilder.Entity<ChildContentRelationship>()
          .HasRequired(ccr => ccr.Parent)
          .WithMany(c => c.ChildContent)
          .HasForeignKey(ccr => ccr.ParentId);
      
      modelBuilder.Entity<ChildContentRelationship>()
          .HasRequired(ccr => ccr.Child)
          .WithMany()
          .HasForeignKey(ccr => ccr.ChildId);
      

Я считаю, что вам нужен вариант 2a или 2b, но яя не уверен.

...