Как создать динамические множественные частичные представления, используя шаблон репозитория в MVC

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

Например, пользователь может выбрать Кентукки из меню, а идентификатор Кентукки равен 1. Домашний контроллер получает идентификатор (1) и определяет возможные модули для этого состояния (простой вызов БД). Возможно, есть модуль объявлений и модуль контактов для государства. Модуль объявлений может иметь несколько элементов, но это только один модуль. Будет частичное представление для каждого типа модуля.

Вот основные настройки, которые у меня есть.

public interface IModuleRepository
{
    IList<MenuItemModule> GetMenuItemModules(int menuItem);
    IList<Announcements> GetAnnouncements(int modID);
    IList<News> GetNews(int modID);
    IList<Contacts> GetContacts(int modID);
}



//business object
public class MenuItemModule
{

    private int _MenuItemID;
    private int _ModuleID;
    private int _ModuleDefID;
    private string _Src;
    private int _ModuleOrder;

//get, set properties for these...

}

//announcements entity
public class Announcements
{
    private int _ID = -1;
    private int _MenuItemID = -1;
    private int _ModuleID = -1;
    private string _Description = string.Empty;

//get set props ...
}

В моем домашнем контроллере...

public class HomeController : Controller
{


    private IModuleRepository modRepository;

    public HomeController(IModuleRepository modRepository)
    {
        this.modRepository = modRepository;
    }


    public ViewResult Item(string ItemID)
    {

        //returns a list of menuitemmodules for the page.  This gives me the Src or name of each
        //module on the page, i.e. Announcements, news, contacts, etc. 
        var modules = modRepository.GetMenuItemModules(Convert.ToInt32(ItemID)); 


        return View(modules);

   }

}

Я пробовал несколько разных моделей возврата, но всегда сталкивался с некоторыми ограничениями. Если я передам модули menuitemmodules в свой Item.aspx, я могу сделать что-то вроде этого:

   foreach (var mod in Model)           
   {               
        Html.RenderPartial(mod.Src, a);      //needs an announcement object though    
   } 

Это делает его несколько динамичным, потому что у меня есть Src, который в основном будет чем-то вроде «Объявлений», и я могу просто создать частичный файл messages.ascx для обработки модуля. Но мне было трудно передать мой модуль меню и объект объявлений.

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

Как я могу решить свою проблему? Надеюсь, я предоставил достаточно информации. Мне нравится основная идея здесь - http://www.mikesdotnetting.com/Article/105/ASP.NET-MVC-Partial-Views-and-Strongly-Typed-Custom-ViewModels, но это работает только для статических модулей на страница.

Я попробовал составную модель представления под названием ModuleViewModel. Вот эта попытка:

public class ModuleViewModel
{
    public IList<Announcements> announcements { get; set; }
    public IList<MenuItemModule> mods { get; set; }

}

Если я передам эту модель в Item.aspx, я смогу сделать что-то вроде этого (но я, должно быть, делаю что-то не так, потому что что-то выглядит не так).

   foreach (var mod in Model)           
   {

       if (mod.announcements.Count > 0)
       {
           Html.RenderPartial("Announcements", mod.announcements);
       }


   } 

Еще раз, масштабируемость будет преследовать меня. Я хотел бы иметь что-то вроде этого на странице элемента:

   foreach (var mod in Model)           
   {

           Html.RenderPartial(mod.Src, mod);          

   } 

Что бы получить правильный частичный вид и передать ему правильную модель.


person rahkim    schedule 25.04.2013    source источник
comment
Вы пробовали использовать Html.DisplayFor(...)? Он отобразит DisplayTemplate, соответствующий типу свойства.   -  person Ian Mercer    schedule 25.04.2013
comment
Я не уверен, как это сделать. Как он узнает, какой тип модуля использовать?   -  person rahkim    schedule 25.04.2013
comment
Вместо передачи идентификаторов или строк вы должны создать сложную модель представления, включающую все модели представлений для различных модулей на странице, а затем перебирать их, вызывая DisplayFor для отображения соответствующего частичного представления для этого Type объекта.   -  person Ian Mercer    schedule 25.04.2013
comment
Это похоже на то, что я пробовал. Я создал составную модель представления под названием ModuleViewModel. Я отредактирую вопрос и опубликую свою попытку внизу. Может быть, вы можете сказать мне, где я ошибся в этом подходе, потому что кажется, что это правильный путь.   -  person rahkim    schedule 25.04.2013


Ответы (2)


Создайте классы модуля, производные от общего базового класса модуля:

public class AnnouncementsModule : Module
{
}

public class ContactsModule : Module
{
}

В контроллере:

Создайте свои различные модули и поместите их в общий модуль представления (здесь у него есть свойство Modules, которое представляет собой массив Module:

var viewModel = new ComplexViewModel 
                { 
                    Modules = new [] 
                    { 
                       new ContactsModule(), 
                       new AnnouncementsModule() 
                    }
                 };
return View(viewModule);

С учетом:

@Html.DisplayFor(x => x.Modules);

Создайте частичные представления для каждого Type модуля в соответствующей папке «Общие». (Запустите его, не создавая их, и он покажет вам исключение с местами, где он их ищет).

person Ian Mercer    schedule 25.04.2013
comment
Позвольте мне попробовать это. Это очень похоже на мою первую попытку, но я не создавал базовый класс модуля. Я думаю, что это могло быть ключом. - person rahkim; 25.04.2013
comment
Быстрый вопрос. Как выглядит массив в ComplexViewModel? Это просто общедоступный модуль [] Modules {get; задавать;} ? - person rahkim; 25.04.2013
comment
У меня ментальный блок по этому поводу. Я сделал это, но у меня нет возможности вставить новый AnnouncementsModule() и т. д. Пока у меня есть публичный модуль [], определенный в моей сложной модели представления, и публичный Announcements AnnouncementsModule {get; set;}. Класс Announcements действительно наследуется от базового модуля. Какую очевидную вещь я здесь упускаю? - person rahkim; 26.04.2013
comment
Я, вероятно, неправильно создаю различные модули и помещаю их в модуль общего представления. Я предположил, что это означает размещение ссылки на каждый модуль в модели комплексного представления, т. е. public Announcements AnnouncementModule { get; задавать; }. Я думаю, что здесь я потерялся. - person rahkim; 26.04.2013
comment
Просто для пояснения, я следовал вышеизложенному точно так, как написано, но когда я добрался до своего представления и набрал Html.DisplayFor(module); Я получил эту ошибку во время разработки: Аргументы типа для метода «System.Web.Mvc.Html.DisplayExtensions.DisplayFor‹TModel,TValue›(System.Web.Mvc.HtmlHelper‹TModel›, System.Linq.Expressions.Expression‹ System.Func‹TModel,TValue››)» нельзя вывести из использования. Попробуйте явно указать аргументы типа. Надеюсь, это поможет. Хотя я вообще в замешательстве. Как передать экземпляр ContactsModule в массив, отображая данные в партиале? - person rahkim; 06.05.2013
comment
@rahkim - Это кажется вам ужасно амбициозным проектом, если вы настолько зелены, что не понимаете основ полиморфизма и выражений. Мы можем вам помочь, но в какой-то момент вам нужно учиться, а не просто копировать. @Html.DisplayFor(m => m.Modules) (выньте цикл for, DisplayFor автоматически перебирает коллекции). Я предлагаю прочитать здесь: bradwilson .typepad.com/blog/2009/10/ - person Erik Funkenbusch; 07.05.2013
comment
@MystereMan - Спасибо за совет. Я на самом деле читал эту ссылку раньше, и это очень полезно. Я решил скопировать приведенный выше код только потому, что все остальное, что я пробовал (включая ваше предложение), не сработало. Я подумал, что должен упустить какой-то нюанс MVC. Я знаком с полиморфизмом, но выражения для меня в новинку, как и MVC. Например, как (в приведенном выше коде) добавление нового ContactsModule() в коллекцию дает представлению необходимые данные? - person rahkim; 07.05.2013
comment
@rahkim - Если бы вы прочитали всю ссылку (все ее части), вы бы поняли, как выбираются шаблоны в зависимости от их типа. MVC просматривает коллекцию модулей, проверяет каждый из них и вызывает для них GetType(), затем ищет шаблон в пути к шаблонам с тем же именем и расширением .cshtml, .vbhtml, .aspx или .ascx. - person Erik Funkenbusch; 07.05.2013
comment
@MystereMan - я полностью понимаю эту часть. Но, возможно, мне нужно перечитать еще раз, потому что я не понимаю, как new ContactsModule() содержит данные, необходимые для шаблона. Мое отключение происходит между вызовом данных в контроллере и отображением выбранных шаблонов этих данных. - person rahkim; 07.05.2013
comment
@rahkim - новый ContactsModule() создает новый экземпляр класса, который вы создаете, с именем ContactsModule, который является производным от вашего базового класса Module. Вы размещаете любые данные, необходимые вашему модулю контактов, в ContactsModule. В своем шаблоне вы строго вводите его в ContactsModule, после чего вы можете получить доступ к этим данным. Я действительно не понимаю, какие у вас проблемы с пониманием этого. - person Erik Funkenbusch; 07.05.2013
comment
@rahkim - О, кажется, я понял. Это опечатка в коде выше. Либо измените его на new Contacts(), либо измените класс с Contacts на ContactsModule. - person Erik Funkenbusch; 07.05.2013
comment
Вот в чем мое непонимание. Вы размещаете любые данные, необходимые вашему модулю контактов, в ContactsModule. Я создал экземпляр ContactsModule и заполнил его контактом. Но если я помещу новый экземпляр ContactsModule в модель представления, переданную странице, я не уверен, как на странице есть соответствующие данные. Похоже, что у него будет массив Module с пустым классом ContactsModule и его свойствами данных. Вот что, кажется, происходит. Мое представление попадает в эту строку - @Html.DisplayFor(x => x.Modules); и продолжается без ошибок... и без загрузки модуля контактов.ascx. - person rahkim; 07.05.2013

После того, как я возился с этим более недели, мне наконец удалось выяснить, как MVC может динамически делать то, что я хочу. Я решил опубликовать свое решение для тех, кто плохо знаком с MVC. Надеюсь, следующее разъяснит мои недоразумения (хотя на данном этапе моего понимания MVC я не могу сказать, что это лучший подход).

Я включу предыдущие фрагменты кода и модификации для ясности:

 public interface IModuleRepository
 {
      IList<MenuItemModule> GetMenuItemModules(int menuItem);
      IList<Announcements> GetAnnouncements(int modID);
      IList<News> GetNews(int modID);
      IList<Contacts> GetContacts(int modID);
 }



//business object
public class MenuItemModule
{

    private int _MenuItemID;
    private int _ModuleID;
    private int _ModuleDefID;
    private string _Src;
    private int _ModuleOrder;

//get, set properties for these...

}

//announcements entity
public class Announcements  : MenuItemModule
{
    private int _ID = -1;
    private string _Description = string.Empty;

//get set props ...
}

Я также добавил еще один класс:

public class AnnouncementModule : MenuItemModule
{
    private IList<Announcements> _Announcements;
    //get set prop
}

... и я создал модель для представления

public class HomeItemViewModel
{
    public MenuItemModule[] MenuItemModules { get; set; } //collection of menuitemmodules
}

В моем домашнем контроллере...

    var menuItemModules = modRepository.GetMenuItemModules(ItemID);

    if (menuItemModules.Count > 0)
    {
        AnnouncementModule aMod;
        MenuItemModule[] mods = new MenuItemModule[menuItemModules.Count()];

        int i = 0;

        //loop through each MenuItemModule assign to the appropriate model
        foreach (MenuItemModule mod in menuItemModules)
        {
            if (mod.Src == "Announcements")
            {
                aMod = new AnnouncementModule();
                aMod.Announcements = modRepository.GetAnnouncements(mod.ModuleID);

                //now add this to the menuitemmodule collection
                mods[i] = aMod;
            }

            if (mod.Src == "Contacts")
            {
                //...
            }

            i++;
        }

    }


    var viewModel = new HomeItemViewModel
    {
        MenuItemModules = mods
    };

    return View(viewModel);

Затем я воспользовался предложением использовать DisplayFor в представлении. Представление строго типизировано для HomeItemViewModel.

<%: Html.DisplayFor(m => m.MenuItemModules) %>

Это перебирает коллекцию и в зависимости от типа вызывает этот шаблон. В этом примере он вызывает AnnouncementModule.ascx, строго типизированный для AnnouncementModule.

foreach (var a in Model.Announcements)
{
    //a.Description will give us the description of the announcement
}

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

person rahkim    schedule 09.05.2013