Несколько моделей в одном представлении (C # MVC3)

#c# #asp.net-mvc #asp.net-mvc-3

#c# #asp.net-mvc #asp.net-mvc-3

Вопрос:

Я использую C # и MVC3.

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

Как я могу отправить обе модели в одно представление?

Ответ №1:

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

Вы должны реализовать предложенное Дэвидом Гленном решение для вашего текущего сценария, а также для всех других представлений, даже если требуется сопоставление модели предметной области с другим классом модели представления.

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

Если у вас есть, скажем, верхнее меню > TopMenu.aspx И внутри него есть несколько частичных представлений > StudentMenu.ascx , ResultMenu.ascx

Вы создадите модель представления для верхнего меню > TopMenuViewModel.cs И вы также создадите модели представления для частичных представлений > StudentMenuViewModel , ResultMenuViewModel и т.д.

и ваша TopMenuViewModel будет иметь обе >

 class TopMenuViewModel 
{
   //all the stuff required in TopMenu.aspx
   StudentMenuViewModel studentvm;
   ResultMenuViewModel resultvm;
}
  

и в TopMenu.aspx при рендеринге части вы передадите соответствующую модель представления >

Html.RenderPartial('StudentView', Model.studentvm)

Надеюсь, это имеет смысл

Комментарии:

1. Привет, спасибо за ваш ответ. Вы имеете в виду здесь: asp.net ? демонстрации / руководства?

2. В основном да, но если вы создаете большой проект, вы всегда должны абстрагировать объекты вашего домена и модели представления.

3. Привет, спасибо за совет, с самого начала планировалось разделить модели предметной области. Но я запутываюсь в том, как я буду отправлять объекты модели с контроллера, особенно когда я разделил свою страницу на несколько частичных представлений. И некоторые частичные представления зависят от определенных моделей. Например, в моем меню частичного просмотра, предполагая, что у меня есть MenuViewModel. Добавьте MenuViewModel в соответствующую большую модель CompilationViewModel (menu, students и т.д.), Как я получу доступ к этой конкретной модели представления в данном представлении? Или, что гораздо хуже, получить доступ к двум моделям представления в одном представлении? Спасибо.

4. Привет, спасибо за пример кода. Это, конечно, полезно, я просто подумал, что смогу обойтись без передачи модели из основного представления, просто получить доступ к модели представления непосредственно из частичного представления.

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

Ответ №2:

Вы можете создать ViewModel, который является представлением вашего представления, а не вашей бизнес-модели

 public class StudentPage {

  public IEnumerable<Student> Students { get; set; }

  public Menu Menu { get; set; }

}
  

Затем ваш контроллер возвращает ViewModel в ваше представление

 public ViewResult Students() {

   var menu = GetMenu();
   var students = Repository.Students();

   var model = new StudentPage {
     Menu = menu,
     Students = students
   }

   return View(model);

}
  

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

 public class BasePage {

  public Menu Menu { get; set; }

}

public class StudentPage : BasePage {

  public IEnumerable<Student> Students { get; set; }

}
  

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

Комментарии:

1. Привет, спасибо за ответ. На самом деле я делаю это с контроллером базового класса, у которого есть определение Menu (при OnActionExecuting). Мой вопрос в том, всегда ли я должен это определять? var model = new StudentPage { Menu = меню, Студенты = students } возвращает представление (модель); Проблема в том, что у меня может быть другой объект, скажем, Teacher, тогда у меня будет страница TeacherPage, которая (учитель меню)? Кстати, как мне отличить 2 модели в представлении? Извините новичка.

2. @czetsuya: В конечном итоге он оказывается наиболее гибким, если у вас есть одна модель представления для каждого представления . Итак, в случае наличия страницы учителя, да, у вас также был бы TeacherPage класс в качестве вашей модели представления, который вы бы создали в действии контроллера для этого представления и вернули в качестве своей модели. Надеюсь, это поможет.

3. Можем ли мы использовать два частичных представления, которые содержат одну модель на главной странице? И в чем отличие от приведенного выше? Спасибо

Ответ №3:

Примечание: Классы, приведенные ниже, должны использоваться в .net 3.5 и ниже, потому что .net 4 представлен как аналогичный класс с именем Tuple и должен использоваться вместо него.

MultiObject<O1, O2, ..> и MultiList<L1, L2, ...>

Вот как я пишу такие действия контроллера и представления:

 public ActionResult MultiModel()
{
    MultiList<User, Company> result = MultiList.New(
        this.repository.GetUsers(),
        this.repository.GetCompanies()
    );
    return View(result);
}
  

И мое представление имеет тип:

 ViewPage<MultiList<User, Company>>
  

Я использую этот удобный класс для повторного использования:

 #region MultiObject static helper class

/// <summary>
/// Provides static methods for creating multi objects with type inference.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
public static class MultiObject
{
    /// <summary>
    /// Creates a new <see cref="MultiObject{T1, T2}"/> object instance.
    /// </summary>
    /// <typeparam name="T1">The type of the first object.</typeparam>
    /// <typeparam name="T2">The type of the second object.</typeparam>
    /// <param name="first"><typeparamref name="T1"/> object instance.</param>
    /// <param name="second"><typeparamref name="T2"/> object instance.</param>
    /// <returns>
    /// Returns a <see cref="MultiObject{T1, T2}"/> of <typeparamref name="T1"/> and <typeparamref name="T2"/> object instances.
    /// </returns>
    public static MultiObject<T1, T2> New<T1, T2>(T1 first, T2 second)
    {
        return new MultiObject<T1, T2>(first, second);
    }

    /// <summary>
    /// Creates a new <see cref="MultiObject{T1, T2, T3}"/> object instance.
    /// </summary>
    /// <typeparam name="T1">The type of the first object.</typeparam>
    /// <typeparam name="T2">The type of the second object.</typeparam>
    /// <typeparam name="T3">The type of the third object.</typeparam>
    /// <param name="first"><typeparamref name="T1"/> object instance.</param>
    /// <param name="second"><typeparamref name="T2"/> object instance.</param>
    /// <param name="third"><typeparamref name="T3"/> object instance.</param>
    /// <returns>
    /// Returns a <see cref="MultiObject{T1, T2, T3}"/> of <typeparamref name="T1"/>, <typeparamref name="T2"/> and <typeparamref name="T3"/> objects instances.
    /// </returns>
    public static MultiObject<T1, T2, T3> New<T1, T2, T3>(T1 first, T2 second, T3 third)
    {
        return new MultiObject<T1, T2, T3>(first, second, third);
    }
}

#endregion

#region MultiObject<T1, T2>

/// <summary>
/// Represents a 2-multi object, or pair.
/// </summary>
/// <typeparam name="T1">The type of the multi object's first component.</typeparam>
/// <typeparam name="T2">The type of the multi object's second component.</typeparam>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
public class MultiObject<T1, T2>
{
    /// <summary>
    /// Gets or sets the value of the first multi object component.
    /// </summary>
    /// <value>The first.</value>
    public T1 First { get; set; }

    /// <summary>
    /// Gets or sets the value of the second multi object component.
    /// </summary>
    /// <value>The second multi object component value.</value>
    public T2 Second { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="MultiObject{T1, T2}"/> class.
    /// </summary>
    /// <param name="first">Multi object's first component value.</param>
    /// <param name="second">Multi object's second component value.</param>
    public MultiObject(T1 first, T2 second)
    {
        this.First = first;
        this.Second = second;
    }
}

#endregion

#region MultiObject<T1, T2, T3>

/// <summary>
/// Creates a new 3-multi object, or triple.
/// </summary>
/// <typeparam name="T1">The value of the first component of the multi object.</typeparam>
/// <typeparam name="T2">The value of the second component of the multi object.</typeparam>
/// <typeparam name="T3">The value of the third component of the multi object.</typeparam>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
[SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")]
public class MultiObject<T1, T2, T3> : MultiObject<T1, T2>
{
    /// <summary>
    /// Gets or sets the value of the third multi object component.
    /// </summary>
    /// <value>The third multi object component value.</value>
    public T3 Third { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="MultiObject{T1, T2, T3}"/> class.
    /// </summary>
    /// <param name="first">Multi object's first component value.</param>
    /// <param name="second">Multi object's second component value.</param>
    /// <param name="third">Multi object's third component value.</param>
    public MultiObject(T1 first, T2 second, T3 third)
        : base(first, second)
    {
        this.Third = third;
    }
}

#endregion
  

Любой случай, когда мне приходится передавать несколько списков

 #region MultiObject static helper class

/// <summary>
/// Provides static methods for creating multi objects with type inference.
/// </summary>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
public static class MultiList
{
    /// <summary>
    /// Creates a new <see cref="MultiObject{T1, T2}"/> object instance.
    /// </summary>
    /// <typeparam name="T1">The type of the first object.</typeparam>
    /// <typeparam name="T2">The type of the second object.</typeparam>
    /// <param name="first"><typeparamref name="T1"/> object instance.</param>
    /// <param name="second"><typeparamref name="T2"/> object instance.</param>
    /// <returns>
    /// Returns a <see cref="MultiObject{T1, T2}"/> of <typeparamref name="T1"/> and <typeparamref name="T2"/> object instances.
    /// </returns>
    public static MultiList<T1, T2> New<T1, T2>(IList<T1> first, IList<T2> second)
    {
        return new MultiList<T1, T2>(first, second);
    }

    /// <summary>
    /// Creates a new <see cref="MultiObject{T1, T2, T3}"/> object instance.
    /// </summary>
    /// <typeparam name="T1">The type of the first object.</typeparam>
    /// <typeparam name="T2">The type of the second object.</typeparam>
    /// <typeparam name="T3">The type of the third object.</typeparam>
    /// <param name="first"><typeparamref name="T1"/> object instance.</param>
    /// <param name="second"><typeparamref name="T2"/> object instance.</param>
    /// <param name="third"><typeparamref name="T3"/> object instance.</param>
    /// <returns>
    /// Returns a <see cref="MultiObject{T1, T2, T3}"/> of <typeparamref name="T1"/>, <typeparamref name="T2"/> and <typeparamref name="T3"/> objects instances.
    /// </returns>
    public static MultiList<T1, T2, T3> New<T1, T2, T3>(IList<T1> first, IList<T2> second, IList<T3> third)
    {
        return new MultiList<T1, T2, T3>(first, second, third);
    }
}

#endregion

#region MultiList<T1, T2>

/// <summary>
/// Represents a 2-multi object, or pair.
/// </summary>
/// <typeparam name="T1">The type of the multi object's first component.</typeparam>
/// <typeparam name="T2">The type of the multi object's second component.</typeparam>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
public class MultiList<T1, T2> : MultiObject<IList<T1>, IList<T2>>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MultiListamp;<T1, T2amp;>"/> class.
    /// </summary>
    /// <param name="first">The first.</param>
    /// <param name="second">The second.</param>
    public MultiList(IList<T1> first, IList<T2> second) : base(first, second) { }
}

#endregion

#region MultiList<T1, T2, T3>

/// <summary>
/// Creates a new 3-multi object, or triple.
/// </summary>
/// <typeparam name="T1">The value of the first component of the multi object.</typeparam>
/// <typeparam name="T2">The value of the second component of the multi object.</typeparam>
/// <typeparam name="T3">The value of the third component of the multi object.</typeparam>
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")]
[SuppressMessage("Microsoft.Design", "CA1005:AvoidExcessiveParametersOnGenericTypes")]
public class MultiList<T1, T2, T3> : MultiObject<IList<T1>, IList<T2>, IList<T3>>
{
    /// <summary>
    /// Initializes a new instance of the <see cref="MultiListamp;<T1, T2, T3amp;>"/> class.
    /// </summary>
    /// <param name="first">The first.</param>
    /// <param name="second">The second.</param>
    /// <param name="third">The third.</param>
    public MultiList(IList<T1> first, IList<T2> second, IList<T3> third) : base(first, second, third) { }
}

#endregion
  

Данные для каждого представления

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

Комментарии:

1. Привет, Роберт, есть ли преимущество вашего мультиобъекта над кортежем<T1, T2 …> и вашего мультилистинга над кортежем<Список<T1>, Список<T2>…

2. @SurjitSamra: Основная причина, по которой я написал эти классы, заключалась в том, что они были нужны мне в .net 3.5. Tuple так появились в .net 4… Но помимо этого, MultiObject не имеет никаких преимуществ, но MultiList имеет причины для краткости. В .net 4 я бы все равно их не использовал…

Ответ №4:

Для обработки нескольких моделей в одном представлении я лично использую ViewBag.

Пожалуйста, обратите внимание, что если вы используете ViewBag, вся помощь, которую вы получаете от компилятора, отключена, и ошибки во время выполнения будут возникать с большей вероятностью, чем если бы свойство было для «обычного» объекта, и компилятор обнаружил бы опечатки.

Это недостаток использования динамических объектов, однако есть много других преимуществ. В вашем контроллере вам просто нужно передать данные / модель в ViewBag:

 public ActionResult Index() {
            ViewBag.TopMenu = TopMenu();
            ViewBag.Student = Student();
            return View();
        }
  

Затем в представлении вызовите их:

 @{
    ViewBag.Title = "Index_ViewBag";
}

<h2>Index View Bag</h2>

<table>
   <tr>
   @foreach (var menu in ViewBag.TopMenu) 
   {
      <td>
      <a href="@menu.URL">@menu.Name</a>
      </td>
   }
   </tr>
</table>

<p>
 <ul>
  @foreach (var student in ViewBag.Student) 
  {
   <li>
    <a href="@student.URL">@student.Name</a>
   </li>   
  }
 </ul>
</p>
  

Ответ №5:

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

 @Html.RenderAction("Menu");
  

Который вызовет собственное действие, генерирующее viewmodel меню и частичное представление «Menu».

Для меня это имеет смысл, но я знаю, что многим это не нравится.

Ответ №6:

В .Net Framework 4.0 вы можете использовать динамические модели.

Примерно:

 public class HomeController : Controller
{
    public ActionResult Index()
    {
        dynamic viewmodel = new ExpandoObject();
        viewmodel.Students = MyStudent();
        viewmodel.MenuItems = MyMenuItems();
        return View(mymodel);
    }
}
  

Как получить доступ к коду представления:

 @model dynamic

@foreach (Student student in Model.Students)
    }
        <h1>@student.Name</h1>
    }
@foreach (MenuItem menuItem in Model.MenuItems)
    {
        <h1>@menuItem.menuname</h1>
    }