Entity Framework, MVC 3, отношение один-ко-многим - PullRequest
2 голосов
/ 23 августа 2011

Хорошо - сначала извинения - я разработчик внешнего интерфейса (HTML, CSS и JS), пытаюсь что-то делать с данными - никогда не бывает красиво!

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

CREATE TABLE [dbo].[Pages] (
    PageId  INT       NOT NULL PRIMARY KEY, 
    [Title] NCHAR(50) NOT NULL
)

CREATE TABLE [dbo].[Series] (
    [SeriesId] INT        NOT NULL,
    [Title]    NCHAR (50) NOT NULL,
    [PageId]   INT        NOT NULL, 
    PRIMARY KEY CLUSTERED ([SeriesId] ASC), 
    CONSTRAINT [FK_Series_Pages] FOREIGN KEY ([PageId]) REFERENCES [Pages]([PageId])
);

CREATE TABLE [dbo].[Collections] (
    [CollectionId] INT        NOT NULL,
    [Title]        NCHAR (50) NOT NULL,
    PRIMARY KEY CLUSTERED ([CollectionId] ASC)
);

CREATE TABLE [dbo].[SeriesCollections] (
    [SeriesCollectionId]   INT    NOT NULL,
    [SeriesId]             INT    NOT NULL, 
    [CollectionId]         INT    NOT NULL, 
    PRIMARY KEY CLUSTERED ([SeriesCollectionId] ASC), 
    CONSTRAINT [FK_SeriesCollections_Series] FOREIGN KEY ([SeriesId]) REFERENCES [Series]([SeriesId]),
    CONSTRAINT [FK_SeriesCollections_Collections] FOREIGN KEY ([CollectionId]) REFERENCES [Collections]([CollectionId])
);

CREATE TABLE [dbo].[Titles] (
    [TitleId]            INT         NOT NULL,
    [Title]              NCHAR (100) NOT NULL,
    [SeriesCollectionId] INT         NOT NULL, 
    PRIMARY KEY CLUSTERED ([TitleId] ASC), 
    CONSTRAINT [FK_Titles_SeriesCollections] FOREIGN KEY ([SeriesCollectionId]) REFERENCES [SeriesCollections]([SeriesCollectionId])

Используя Entity Framework у меня есть следующее:

public DbSet<Page> Pages { get; set; }
public DbSet<Series> Series { get; set; }
public DbSet<Collection> Collections { get; set; }
public DbSet<SeriesCollection> SeriesCollections { get; set; }
public DbSet<Title> Titles { get; set; }

В представлении я хочу получить следующее.

Для данной «Страницы» (id) я хочу, чтобы все «Серии» и внутри каждой из этих «Серий» могли перечислять каждое «Заголовок» и связанную с ним «Коллекцию».

Прежде всего - правильно ли настроен мой БД? Во-вторых, я борюсь с db call и viewmodels, которые вернут это.

Если кто-нибудь может помочь, это было бы здорово

Заранее спасибо

1 Ответ

5 голосов
/ 24 августа 2011

Коллекция может содержать одно или несколько «Титулов».

Из-за этого я бы изменил схему таблицы вашей БД:

  • В таблице Titles заменить [SeriesCollectionId] на [CollectionId], непосредственно ссылаясь на таблицу Collections.

  • В таблице SeriesCollections удалите свой PK [SeriesCollectionId] и оставьте вместо двух оставшихся полей [SeriesId] и [CollectionId] составной первичный ключ.

  • Теперь вы можете смоделировать отношения «многие ко многим» между Series и Collections с EF. Тогда объединительный стол SeriesCollections больше не является частью вашей модели. Это просто скрытая таблица в БД, которой управляет EF. Поэтому вы можете удалить public DbSet<SeriesCollection> SeriesCollections { get; set; }.

Классы моделей могут выглядеть следующим образом:

public class Page
{
    public int PageId { get; set; }
    [Required]
    [MaxLength(50)]
    pubic string Title { get; set; }
    public ICollection<Series> Series { get; set; }
}

public class Series
{
    public int SeriesId { get; set; }
    [Required]
    [MaxLength(50)]
    pubic string Title { get; set; }
    public int SeriesId { get; set; }
    public int PageId { get; set; } // FK property, helpful but not required
    public Page Page { get; set; }
    public ICollection<Collection> Collections { get; set; }
}

public class Collection
{
    public int CollectionId { get; set; }
    [Required]
    [MaxLength(50)]
    pubic string Title { get; set; }
    public ICollection<Series> Series { get; set; }
    public ICollection<Title> Titles { get; set; }
}

public class Title
{
    public int TitleId { get; set; }
    [Required]
    [MaxLength(100)]
    pubic string TTitle { get; set; } // must be other name then class
    public int CollectionId { get; set; } // FK property
    public Collection Collection { get; set; }
}

Для сопоставления «многие ко многим» вам необходим Fluent API:

public class MyContext : DbContext
{
    public DbSet<Page> Pages { get; set; }
    public DbSet<Series> Series { get; set; }
    public DbSet<Collection> Collections { get; set; }
    public DbSet<Title> Titles { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Series>()
            .HasMany(s => s.Collections)
            .WithMany(c => c.Series)
            .Map(a =>
            {
                a.MapLeftKey("SeriesId");
                a.MapRightKey("CollectionId");
                a.ToTable("SeriesCollections");
            });
    }
}

EF выяснит все остальные отношения по соглашению, я считаю.

Для данной «Страницы» (id) я хочу все «Серии» и внутри каждого из те «серии», быть в состоянии перечислить каждое из «Титулов» и его связанная «Коллекция».

С моделью выше вы можете попробовать:

var page = context.Pages.Where(p => p.PageId == id)
    .Include(p => p.Series.Select(s => s.Collections.Select(c => c.Titles)))
    .SingleOrDefault();

Будет выбрана страница, содержащая список серий со списком коллекций со списком заголовков.

Не уверен, что это именно то, что вам нужно, просто непроверенная отправная точка.

(Кстати: вы можете сначала написать свои классы (Code-First) и позволить EF создавать таблицы базы данных. На этапе проектирования проще, когда вы хотите попробовать некоторые отображения, imo.)

Редактировать

Одна вещь, которую я забыл: если вы действительно хотите нефиксированные строковые поля фиксированной длины (NCHAR(50)), вы должны явно указать это в Fluent API. По умолчанию EF будет принимать NVARCHAR(50) полей с отображением выше. Установка столбцов фиксированной длины будет выглядеть следующим образом:

modelBuilder.Entity<Page>().Property(p => p.Title).IsFixedLength();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...