Отношения многие ко многим с Entity Framework и MVC с упорядочением - PullRequest
3 голосов
/ 06 июля 2011

Я использую Code First с POCO с Entity Framework 4 для создания отношения «многие ко многим». Таблица JOIN создана правильно, чтобы связать две сущности. Однако мне нужен дополнительный столбец в таблице JOIN для создания порядка в отношениях. Любой способ заархивировать это? Как следует изменить объекты POCO, чтобы включить дополнительный столбец индекса в таблицу JOIN, чтобы разрешить упорядочение? Возможно ли это даже с Code First? Если нет, то как мне действовать?

public class Product
{
    public Guid ID { get; set; }
    public virtual IList<Navigation> Navigations { get; set; }
}

public class Navigation
{
    public Guid ID { get; set; }
    public virtual IList<Product> Products { get; set; }
}

Автоматически создается таблица со следующими столбцами, там я хочу дополнительный столбец для заказа:

NavigationProducts
    Navigation_ID
    Product_ID

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

Ответы [ 2 ]

3 голосов
/ 15 августа 2012

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

Я использовал два термина Webpage и Widget в этом решении, и, чтобы уточнить, я ожидаю следующего поведения:

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

Фон


В этом примере я использовал 4 сущности, хотя это решение основано на использовании 1 сущности в качестве основы. Эта сущность сопоставляется со своей собственной таблицей и предоставляет 2 таблицы объединения, которые выходят из нее - и они переходят к таблицам для двух сущностей, для которых я изначально хотел установить отношение «многие ко многим».

Существуют следующие сущности:

IndexedWebpageWidget: Это важный объект. Обе Конкретные сущности, приведенные ниже, имеют отношение «многие к одному» с этой сущностью.

BaseUnit: Абстрактный базовый класс для Веб-страница и Виджет - (содержит свойства Id, Title и Content)

Веб-страница: Бетон BaseUnit подкласс, содержащий одно дополнительное свойство (Список IndexedWebpageWidget ).

Виджет: То же, что и веб-страница, также содержащая одно дополнительное свойство (также список IndexedWebpageWidget ).


Объекты


BaseUnit:

public abstract class BaseUnit
    {
        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }

        [Required, MaxLength(300)]
        public string Title { get; set; }

        [Required, AllowHtml]
        public string Content { get; set; }
    }

Виджет и веб-страница:

public class Widget : BaseUnit
{
    [InverseProperty("Widget")]
    public virtual List<IndexedWebpageWidget> Webpages { get; set; }
}

public class Webpage : BaseUnit
{
    [InverseProperty("Webpage")]
    public virtual List<IndexedWebpageWidget> Widgets { get; set; }
}

IndexedWebpageWidget:

public class IndexedWebpageWidget
{
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required]
    public int Order { get; set; }

    [Required]
    [InverseProperty("Widgets")]
    public virtual Webpage Webpage { get; set; }

    [Required]
    [InverseProperty("Webpages")]
    public virtual Widget Widget { get; set; }
}

Атрибуты [InverseProperty] здесь указывают Entity Framework, какие цели есть какие. Это, вероятно, не нужно, так как знает, какой конец One-To-Many является основным. первичные ключи - это внешние ключи.


Тестирование


Я создал быстрый DbContext и DBInitialiser и использовал его для заполнения БД. Это выглядит так:

public class WebWidgetTestContext : DbContext
    {
        public DbSet<Webpage> Webpages { get; set; }
        public DbSet<Widget> Widgets { get; set; }
        public DbSet<IndexedWebpageWidget> WebpageWidgets { get; set; }
    }

    public class WebWidgetInitialiser : DropCreateDatabaseIfModelChanges<WebWidgetTestContext>
    {
        protected override void Seed(WebWidgetTestContext context)
        {
            //
            // create 3 pages
            //

            var webpageA = new Webpage
            {
                Title = "Webpage A",
                Content = "Content for Webpage A",
                Widgets = new List<IndexedWebpageWidget>()
            };

            var webpageB = new Webpage
            {
                Title = "Webpage B",
                Content = "Content for Webpage B",
                Widgets = new List<IndexedWebpageWidget>()
            };

            var webpageC = new Webpage
            {
                Title = "Webpage C",
                Content = "Content for Webpage C",
                Widgets = new List<IndexedWebpageWidget>()
            };


            //
            // create 3 widgets
            //

            var widget1 = new Widget
            {
                Title = "Widget 1",
                Content = "Content for Widget 1",
                Webpages = new List<IndexedWebpageWidget>()
            };

            var widget2 = new Widget
            {
                Title = "Widget 2",
                Content = "Content for Widget 2",
                Webpages = new List<IndexedWebpageWidget>()
            };

            var widget3 = new Widget
            {
                Title = "Widget 3",
                Content = "Content for Widget 3",
                Webpages = new List<IndexedWebpageWidget>()
            };


            // now match them up

            var map1 = new IndexedWebpageWidget
            {
                Webpage = webpageA,
                Widget = widget1,
                Order = 1
            };

            var map2 = new IndexedWebpageWidget
            {
                Webpage = webpageA,
                Widget = widget2,
                Order = 2
            };

            var map3 = new IndexedWebpageWidget
            {
                Webpage = webpageB,
                Widget = widget1,
                Order = 1
            };

            var map4 = new IndexedWebpageWidget
            {
                Webpage = webpageB,
                Widget = widget3,
                Order = 3
            };

            var map5 = new IndexedWebpageWidget
            {
                Webpage = webpageC,
                Widget = widget2,
                Order = 2
            };

            var map6 = new IndexedWebpageWidget
            {
                Webpage = webpageC,
                Widget = widget3,
                Order = 1
            };

            // add
            context.WebpageWidgets.Add(map1);
            context.WebpageWidgets.Add(map2);
            context.WebpageWidgets.Add(map3);
            context.WebpageWidgets.Add(map4);
            context.WebpageWidgets.Add(map5);
            context.WebpageWidgets.Add(map6);

            // save
            context.SaveChanges();

        }
    }

_

Вы можете видеть, что я добавил 3 DBSets в контекст. Я сделал это, чтобы мне было легко заполнить базу данных в инициализаторе.

Обратите внимание, что map5 и map6 назначают различный порядок виджетам для веб-страницы C ...

Стоит также отметить, что Entity Framework создает только 3 таблицы - dbo.IndexedWebpageWidgets , dbo.Webpages и dbo.Widgets . Таблица для IndexedWebpageWidgets действует точно так же, как одна таблица соединения, с добавленным полем заказа.

_

Наконец, для быстрого просмотра я изменил свой домашний контроллер:

public class HomeController : Controller
{
    private WebWidgetTestContext db = new WebWidgetTestContext();

    public ActionResult Index()
    {
        var model = db.Webpages.ToList();

        return View(model);
    }
}

А на мой взгляд:

@model List<WebpageWidgetTest.Models.Webpage>
@{
    ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
<div>
    @{
        foreach (var webpage in Model)
        {
        <div>
            <h4>@webpage.Title</h4>
            <p>@webpage.Content</p>
            <p>Applicable Widgets:</p>
            @foreach (var widget in webpage.Widgets.OrderBy(w => w.Order))
            {
                <div>
                    <h5>@widget.Widget.Title (@widget.Order)</h5>
                    <p>@widget.Widget.Content</p>
                </div>
            }
        </div><hr /><br /><br />
        }
    }
</div>

В коде Razor над ViewModel приведен список всех веб-страниц. Я перебираю их и выписываю скалярные свойства. Затем для каждого IndexedWebpageWidget в свойстве Widgets я зацикливаю и выписываю виджеты по порядку. Замена значения свойства order в БД приведет к тому, что они появятся в разных порядках.

-

Это в основном приводит к следующему HTML:

Webpage A

Content for Webpage A

Applicable Widgets:

Widget 1 (1)

Content for Widget 1

Widget 2 (2)

Content for Widget 2



Webpage B

Content for Webpage B

Applicable Widgets:

Widget 1 (1)

Content for Widget 1

Widget 3 (3)

Content for Widget 3



Webpage C

Content for Webpage C

Applicable Widgets:

Widget 3 (1)

Content for Widget 3

Widget 2 (2)

Content for Widget 2

-

На веб-странице C вы можете видеть, что виджет 3 находится перед виджетом 2, на основе его свойства заказа (которое отображается рядом с заголовком каждого виджета в скобках) ... Замена порядка для одного виджета на веб-странице не повлияет на его заказ для другой веб-страницы. Поэтому все требования были выполнены.

Единственный реальный недостаток этого подхода в том, что у вас есть одна дополнительная сущность.Это на самом деле полезно, хотя для перемещения между двумя объектами, которые он связывает вместе.Я не уверен, что это можно сделать иначе.Каждая сторона отношения должна быть сопоставлена, поэтому я мог бы просто добавить все свойства навигации в класс BaseUnit.

_

В любом случае, я надеюсь, это поможет.

Ура,

Бен

1 голос
/ 06 июля 2011

Я думаю, что именно здесь шаблоны T4 пригодятся. Также посмотрите Настройка классов сущностей в VS 2010 .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...