ASP.NET MVC: привязка к нескольким моделям

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

У меня есть PersonModel, PersonController и куча представлений, которые позволяют пользователю добавлять нового человека, редактировать человека и искать людей.

Я не использую базу данных в бэкэнде. Все, что я делаю, зависит от внешней DLL, которая возвращает структуры "person" (которые я превращаю в PersonModels).

Чтобы искать людей, я должен предоставить структуру человека, которая действует как критерий поиска для метода во внешней DLL. Метод возвращает набор персон-структур, соответствующих критериям поиска. Если я хочу получить всех людей в системе, я передаю методу пустую структуру человека.

Итак, у меня работает функция "получить всех людей"..... но я хотел бы предоставить расширенный поиск.

Мое представление поиска привязано к классу, который содержит 2 свойства:

Public Class PersonSearchModel
  Private _searchCriteria As PersonModel
  Private _searchResults As List(Of PersonModel)
  Public Property SearchCriteria As PersonModel
    Get
      return _searchCriteria
    End Get
    Set(ByVal value As PersonModel)
      _searchCriteria = value
    End Set
  End Property
  Public Property SearchResults As List(Of PersonModel)
    Get
      return _searchResults 
    End Get
    Set(ByVal value As List(Of PersonModel))
      _searchResults = value
    End Set
  End Property
End Class

Теперь представление поиска привязывается к этой модели PersonSearchModel, и у меня есть 2 раздела... раздел, в котором пользователь может указать критерии поиска, и раздел, в котором отображаются результаты поиска.

У меня возникла проблема с привязкой PersonSearchModel.SearchCriteria к элементам управления, используемым для отображения/сбора критериев поиска человека.

Я не могу получить критерии поиска.

Это то, что я считаю критериями поиска:

 <fieldset>
        <legend>Search Criteria</legend>
        <%
            With Model.SearchCriteria
         %>
        <div style="float:left">
        <p>
            <label for="FirstName">
                FirstName:</label>
            <%=Html.TextBox("FirstName", Html.Encode(.FirstName))%>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <label for="LastName">
                LastName:</label>
            <%=Html.TextBox("LastName", Html.Encode(.LastName))%>
            <%= Html.ValidationMessage("LastName", "*") %>
        </p>
         <!-- More controls -->
        </div>
        <%  End With%>
    </fieldset>
     <%=Html.ActionLink("Search", "Search",Model.SearchCriteria)%>
<!-- The Search Results Section-->

PersonModel, переданный в метод Search, является новым/пустым объектом PersonModel. Он не содержит данных, которые ввел пользователь.

Что я здесь делаю неправильно?

********** Редактировать ********** Я попытался изменить вид, чтобы привязать его по-другому. Я удалил ВБ "С":

 <fieldset>
        <legend>Search Criteria</legend>
        <div style="float:left">
        <p>
            <label for="FirstName">
                FirstName:</label>
            <%=Html.TextBox("FirstName", Html.Encode(.FirstName))%>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <label for="LastName">
                LastName:</label>
            <%=Html.TextBox("LastName", Html.Encode(.LastName))%>
            <%= Html.ValidationMessage("LastName", "*") %>
        </p>
         <!-- More controls -->
        </div>
    </fieldset>
     <%=Html.ActionLink("Search", "Search",Model.SearchCriteria)%>
<!-- The Search Results Section-->

Но это не помогло.

Я также пробовал:

 <fieldset>
        <legend>Search Criteria</legend>
        <div style="float:left">
        <p>
            <label for="FirstName">
                FirstName:</label>
            <%=Html.TextBox("Model.SearchCriteria.FirstName", Html.Encode(Model.SearchCriteria.FirstName))%>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <label for="LastName">
                LastName:</label>
            <%=Html.TextBox("Model.SearchCriteria.LastName", Html.Encode(Model.SearchCriteria.LastName))%>
            <%= Html.ValidationMessage("LastName", "*") %>
        </p>
         <!-- More controls -->
        </div>
    </fieldset>
     <%=Html.ActionLink("Search", "Search",Model.SearchCriteria)%>
<!-- The Search Results Section-->

И:

 <fieldset>
        <legend>Search Criteria</legend>
        <div style="float:left">
        <p>
            <label for="FirstName">
                FirstName:</label>
            <%=Html.TextBox("SearchCriteria.FirstName")%>
            <%= Html.ValidationMessage("FirstName", "*") %>
        </p>
        <p>
            <label for="LastName">
                LastName:</label>
            <%=Html.TextBox(".SearchCriteria.LastName")%>
            <%= Html.ValidationMessage("LastName", "*") %>
        </p>
         <!-- More controls -->
        </div>
    </fieldset>
     <%=Html.ActionLink("Search", "Search",Model.SearchCriteria)%>
<!-- The Search Results Section-->

Тем не менее, я все еще получаю пустую/новую модель PersonModel, переданную в метод поиска в контроллере. Я также проверил PersonSearchModel.SearchCriteria, чтобы увидеть, возможно ли, что он содержит введенные значения, но у него также есть новая/пустая PersonModel.

-Фринни


person Frinavale    schedule 30.11.2009    source источник


Ответы (4)


Использование отражения - это в значительной степени то, для чего настроены связыватели моделей MVC, я предполагаю, что вы неправильно называли свои поля, поэтому, когда они возвращались к вашему действию, они не сопоставлялись с вашими параметрами. Попробуйте сделать что-то вроде:

Function Search(ByVal personSearchModel As PersonSearchModel, ByVal collection As FormCollection) As ActionResult

Тогда ваши поля (HTML) должны называться так:

<%= Html.TextBox("personSearchModel.SearchCriteria.FirstName", Html.Encode(Model.SearchCriteria.FirstName)) %>
person Matt J    schedule 07.12.2009
comment
Не уверен, к чему ты клонишь. Меня немного смущает ваш выбор имени переменной... имя переменной соответствует типу. Поэтому я не уверен, пытаетесь ли вы сказать мне сопоставить имя переменной с именем поля или вы говорите мне сопоставить тип объекта. Я попробую оба способа и посмотрю, что произойдет. Спасибо за ваш ответ. - person Frinavale; 08.12.2009
comment
Большое спасибо, Мэтт. Оказывается, совпадать должно имя переменной. Я, вероятно, должен взять книгу на эту тему вместо того, чтобы сначала пробовать что-то на практике. Еще раз спасибо! - person Frinavale; 08.12.2009
comment
Я просматриваю другие свои представления (редактирование и создание), и я не указываю имя переменной в именах полей для этих представлений... но я все еще получаю полностью заполненный объект человека, переданный в качестве параметра для методы создания или редактирования. Почему это так? - person Frinavale; 08.12.2009

Я думаю, что вам не хватает необходимых префиксов при вызовах Html.TextBox и Html.ValidationMessage. Я рекомендую не использовать ключевое слово "With" в VB, поскольку оно скрывает полное имя члена. Как помощникам HTML, так и привязке модели (которая используется для передачи параметров в методы действия) требуется полное имя свойства или поля, чтобы получить значение.

Попробуйте это вместо этого:

<%= Html.TextBox("SearchCriteria.FirstName", SearchCriteria.FirstName) %>
<%= Html.ValidationMessage("SearchCriteria.FirstName", "*") %>

Кроме того, нет необходимости вызывать Html.Encode() для значения, передаваемого в TextBox — оно все равно автоматически кодируется.

person Eilon    schedule 01.12.2009
comment
Я удалил VB.NET и попробовал код, который вы рекомендовали. Я также пробовал ‹%= Html.TextBox(Model.SearchCriteria.FirstName, Model.SearchCriteria.FirstName) %› Но это не помогло. Я по-прежнему ничего не меняю: я все еще получаю новую/пустую модель PersonModel в методе поиска. - person Frinavale; 02.12.2009
comment
Вы должны иметь возможность просто использовать Html.TextBox(SearchCriteria.FirstName) (не добавляйте к строке префикс Model. И вам также не нужно передавать явное значение - помощник TextBox автоматически извлечет его. - person Eilon; 05.12.2009
comment
Контроллер все еще получает пустой/новый режим PersonMode. Пожалуйста, смотрите исходный вопрос, потому что я обновил его, включив в него то, что еще пробовал. - person Frinavale; 07.12.2009

После долгих тестов и отладки я обнаружил кое-что интересное: я могу получить информацию, введенную пользователем, из FormCollection, переданной в функцию поиска. Изначально моя функция поиска принимала 2 параметра. Первым параметром была PersonModel, которая должна была быть привязана к PersonSearchModel.SearchCriteria, вторым параметром была FormCollection для представления.

Я могу создать PersonModel, используемую для PersonSearchModel.SearchCriteria, на основе FormCollection, переданного в функцию поиска. Я удалил первый параметр (PersonModel), так как это всегда был новый/пустой объект.

Это мой текущий метод поиска:

<AcceptVerbs(HttpVerbs.Post)> _
Function Search(ByVal collection As FormCollection) As ActionResult
        Dim searchModel As New SearchPersonsModel

        Dim personProperties() As PropertyInfo = GetType(PersonModel).GetProperties
        For Each pi As PropertyInfo In personProperties
            Dim piName As String = pi.Name
            Dim info As String = Array.Find(collection.AllKeys, Function(x) x.Compare(piName, x, true) = 0)
            If String.IsNullOrEmpty(info) = False Then
                pi.SetValue(searchModel.SearchCriteria, collection.Item(info), Nothing)
            End If
        Next
'The following code uses the searchModel.searchCriteria to search for People.
End Function

Мой вид (если вам интересно) выглядит так:

 <% Using Html.BeginForm()%>
 <%With Model.SearchCriteria%>
  <fieldset>
    <legend>Search Criteria</legend>
      <div style="float: left">
        <p>
          <label for="FirstName">FirstName:</label>
          <%=Html.TextBox("FirstName", Html.Encode(Model.SearchCriteria.FirstName))%>
           <%=Html.ValidationMessage("Model.SearchCriteria.FirstName", "*")%>
        </p>
        <p>
            <label for="LastName">LastName:</label>
            <%=Html.TextBox("LastName", Html.Encode(Model.SearchCriteria.LastName))%>
            <%=Html.ValidationMessage("Model.SearchCriteria.LastName", "*")%>
        </p>
      <!---..... more controls .... -->
    </div>
  </fieldset>
  <%End With%>
  <input type="submit" value="Search" />

<!-- Search Results Controls -->

  <%End Using%>

Это решение работает, но я действительно не доволен им. Почему я должен воссоздавать PersonModel, используемый в качестве критерия поиска? Почему я не мог передать этот объект в качестве параметра в метод поиска?

-Фринни

person Frinavale    schedule 07.12.2009

Похоже, что UpdateModel() может быть здесь вашим другом. MVC не передает объекты в стиле веб-форм.

Даже если ваша модель состоит из двух объектов, вполне возможно использовать UpdateModel для получения значений для одного из них. Вам просто нужно указать этот объект в качестве параметра. Например.:

Thing t = new Thing();
UpdateModel(t);

Возможно, вам придется посмотреть на имена параметров, чтобы MVC правильно угадал. Кроме того, вам, возможно, придется добавить свойства в белый список из соображений безопасности и/или чтобы избежать чрезмерно тщательной проверки модели.

person Peder Skou    schedule 07.12.2009
comment
Я пробовал это, но это все еще не работает. Я просмотрел метод UpdateModel, и похоже, что он должен работать... но по какой-то причине он все еще возвращает новый/пустой объект. - person Frinavale; 08.12.2009