Биндинг сложных объектов в ASP.NET MVC

Механизм биндинга данных в ASP.NET MVC мощный, и позволяет биндить не только простые типы, но и достаточно сложные классы. В данной статье будут рассмотрены 4 сценария. Опыт разработки ASP.NET MVC приложений у меня небольшой, но с данными задачками я уже столкнулся.

Подготовка

Запускаем студию и создаем приложение на основе шаблона ASP.NET MVC Application. Далее создаем новый контроллер, пусть это будет StarsController.

Далее откройте Views\Home\Index и замените код следующим:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
         Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="indexTitle" 
             ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>
<asp:Content ID="indexContent" 
             ContentPlaceHolderID="MainContent" runat="server">
    <%= Html.ActionLink("Передача списка строк","Index","Stars") %>
    <%= Html.ActionLink("Передача простого объекта","SimpleObject","Stars") %>
    <%= Html.ActionLink("Передача списка простых объектов","SimpleObjectList","Stars") %>
    <%= Html.ActionLink("Передача сложного объекта","ComplexObject","Stars") %>

</asp:Content>

Передача массива/списка простых значений

Это может пригодиться если нужно предоставить пользователю сформировать список чего либо. Для простоты список я сформирую сразу же, можно прикрутить jQuery и сделать красиво, а можно сделать проще ;-)

В нашем случае пусть это будет список звезд, благо на выходных я наконец узнал где находиться Полярная!
Вот список подопытных: Солнце, Бетельгейзе, Арктур и Полярная звезда.

Контроллер
Добавим в контроллер метод ShowList, который и будет принимать отправленные данные. Можно использовать как List<> т

public ActionResult ShowList(List<string> stars)
	{
		return View();
	}

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

Представление
Открываем созданный ранее контроллер StarsController и для его метода Index создадим View.

Добавим в тело представления следующий код:

   
<% using (Html.BeginForm("ShowList", "Stars"))
       { %>
            <input name="stars" value="Солнце" /><br />
            <input name="stars" value="Бетельгейзе" /><br />
            <input name="stars" value="Арктур" />     <br />
            <input name="stars" value="Полярная звезда" />    <br />
    
            <input type="submit" value="Отправить список" />
    <%} %>

Мы получили то что нужно, то же самое можно проделать с числами и другими простыми типами. Кроме типизированного списка можно использовать: T[], IEnumerable, IList, List, ICollection и Collection. Те метод ShowList можно изменить следующим образом:

public ActionResult ShowList(IEnumerable<string> stars)
	{
		return View();
	}

Передача простого объекта

А теперь передадим в метод контроллера простой класс:

using System;

namespace MvcAppBindingEx.Models
{
	public class SimpleObject
	{
		public string Name { get; set; }
		public double Value { get; set; }
	}
}

Контроллер
Создаем метод контроллера.

	public ActionResult SimpleObject(Models.SimpleObject data)
	{
		return View();
	}

Представление

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	SimpleObject
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>SimpleObject</h2>
     <% using (Html.BeginForm())
       { %>
            <input name="data.Name" value="η Киля" /><br />
            <input name="data.Value" value="6,21" /><br />
            
            <input type="submit" value="Отправить" />
    <%} %>

</asp:Content>

Запускаем и смотрим что возвращается из представления.

Как видно из кода формы, поля имеют названия вида [имя параметра].[Название свойства класса]. Это и используется механизмом биндинга для корректной привязки данных и класса.

Передача списка простых объектов

Теперь чуть сложнее задача — нужно передать список простых объектов.

Контроллер

public ActionResult SimpleObjectList(List<Models.SimpleObject> list)
	{
		return View();
	}

Представление
В этом случае нужно использоват индексное поле для формирования имени полей в виде [parameterName][Index].[PropertyName]

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
	SimpleObjectList
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>SimpleObjectList</h2>
    
    <% using (Html.BeginForm())
       { %>
            Название: <input name="list[0].Name" value="η Киля" />
            Расстояние: <input name="list[0].Value" value="6,21" /> св. лет<br />
            
            Название: <input name="list[1].Name" value="αС Центавра" />
            Расстояние: <input name="list[1].Value" value="4,225" /> св. лет<br />
            
            <input type="submit" value="Отправить" />
    <%} %>
</asp:Content>

Результат

Передача сложного объекта

В данном случае в качестве сложного объекта выбран класс ComplexObject. Который имеет несколько полей, одно из которых содержит список

Контроллер

public ActionResult ComplexObject(Models.ComplexObject data)
	{
		return View();
	}

Представление
В передставлении заранее заданы поля объекта и массив простых объектов. По сути это комбинация вышеприведенных способов передачи простого объекта и списка объектов.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    ComplexObject
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <h2>
        ComplexObject</h2>
    <% using (Html.BeginForm())
       { %>
       <strong>Созвездие</strong> <br/>
    Название: <input name="data.Name" value="Волопас" /><br />
    Аббревиатура: <input name="data.Abbreviation" value="Boo" /><br/>
    <br/>
        
    <strong>Ярчайшие звёзды</strong><br/>
    <p>
        Название: <input name="data.Stars[0].Name" value="Арктур" />
        Расстояние: <input name="data.Stars[0].Value" value="36,7" /> св. лет<br />
        Название: <input name="data.Stars[1].Name" value="Муфрид" />
        Расстояние: <input name="data.Stars[1].Value" value="37" /> св. лет<br />
        Название: <input name="data.Stars[2].Name" value="Ицар" />
        Расстояние: <input name="data.Stars[2].Value" value="270" /> св. лет<br />
    </p>
    <input type="submit" value="Отправить" />
    <%} %>
</asp:Content>

Результат

Исходники проекта с примерами.

Ссылки:
0. Models and Validation in ASP.NET MVC
1. Phil Haack — Model Binding To A List
2. Scot Hanselman — ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries

  • Pingback: MVC Binding (привязка данных) | I'm KarAn()

  • JohnyMotorhead

    спасибо за статью!! благодаря ей разобрался :)

  • Аноним

    Рад что статья пригодилась!

  • Haik Mnatsakanyan

    thn for art

  • Виталий Дьяченко

    А массив объектов вместо списка получить можно?

  • AlexandrYZ

    Да, с массивом так же. Единственная проблема если на стороне фронтенда из массива строк удаляются строчки, то в таком случае стандартный биндинг ASP.NET MVC не сработает. Придется либо перенумеровать массив, либо написать свой ModelBinder.