Поделиться через


Изучение методов Edit и представления Edit (VB)

Рик Андерсон

В этом руководстве описаны основы создания веб-приложения MVC ASP.NET MVC с помощью Microsoft Visual Web Developer 2010 Express с пакетом обновления 1 (SP1), который является бесплатной версией Microsoft Visual Studio. Перед началом работы убедитесь, что вы установили необходимые компоненты, перечисленные ниже. Все их можно установить, щелкнув следующую ссылку: установщик веб-платформы. Кроме того, можно установить предварительные требования по отдельности, используя следующие ссылки:

Если вы используете Visual Studio 2010 вместо Visual Web Developer 2010, установите необходимые компоненты, щелкнув следующую ссылку: предварительные требования Visual Studio 2010.

Проект Visual Web Developer с VB.NET исходный код доступен для сопровождения этого раздела. Скачайте версию VB.NET. Если вы предпочитаете C#, перейдите к версии C# этого руководства.

В этом разделе вы изучите созданные методы действий и представления для контроллера фильма. Затем вы добавите настраиваемую страницу поиска.

Запустите приложение и перейдите к Movies контроллеру, добавив /Movies к URL-адресу в адресной строке браузера. Удерживайте указатель мыши на ссылку "Изменить ", чтобы просмотреть URL-адрес, на который он ссылается.

Снимок экрана: приложение перемещения MVC с ссылкой

Ссылка "Изменить" была создана методом Html.ActionLink в представлении Views\Movies\Index.vbhtml:

@Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |

Снимок экрана: Html.ActionLink в редакторе кода.

Объект Html является вспомогательным объектом, который предоставляется с помощью свойства в базовом WebViewPage классе. Метод ActionLink вспомогательного средства упрощает динамическое создание гиперссылок HTML, которые связываются с методами действий на контроллерах. Первым аргументом метода ActionLink является текст ссылки для отрисовки (например, <a>Edit Me</a>). Второй аргумент — это имя вызываемого метода действия. Последним аргументом является анонимный объект , который создает данные маршрута (в данном случае — идентификатор 4).

Созданная ссылка, показанная на предыдущем изображении http://localhost:xxxxx/Movies/Edit/4. Маршрут по умолчанию принимает шаблон {controller}/{action}/{id}URL-адреса. Таким образом, ASP.NET преобразуется http://localhost:xxxxx/Movies/Edit/4 в запрос Edit к методу действия контроллера Movies с параметром ID , равным 4.

Можно также передать параметры метода действия с помощью строки запроса. Например, URL-адрес http://localhost:xxxxx/Movies/Edit?ID=4 также передает параметр ID 4 Edit методу действия контроллера Movies .

EditQueryString

Movies Откройте контроллер. Ниже показаны два Edit метода действия.

'
' GET: /Movies/Edit/5

Function Edit(id As Integer) As ViewResult
    Dim movie As Movie = db.Movies.Find(id)
    Return View(movie)
End Function

'
' POST: /Movies/Edit/5

<HttpPost()>
Function Edit(movie As Movie) As ActionResult
    If ModelState.IsValid Then
        db.Entry(movie).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(movie)
End Function

Обратите внимание на второй метод действия Edit, которому предшествует атрибут HttpPost. Этот атрибут указывает, что перегрузка Edit метода может вызываться только для запросов POST. Атрибут можно применить HttpGet к первому методу редактирования, но это не обязательно, так как это значение по умолчанию. (Мы будем ссылаться на методы действий, неявно назначенные атрибуту HttpGet в качестве HttpGet методов.)

Метод HttpGet Edit принимает параметр идентификатора фильма, ищет фильм с помощью метода Entity Framework Find и возвращает выбранный фильм в представление "Изменить". Если в представлении редактирования создана система формирования шаблонов, она проверяет класс Movie и создает код для отображения элементов <label> и <input> для каждого свойства класса. В следующем примере показано представление "Изменить", созданное:

@ModelType MvcMovie.Movie

@Code
    ViewData("Title") = "Edit"
End Code

<h2>Edit</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@Using Html.BeginForm()
    @Html.ValidationSummary(True)
    @<fieldset>
        <legend>Movie</legend>

        @Html.HiddenFor(Function(model) model.ID)

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.Title)
            @Html.ValidationMessageFor(Function(model) model.Title)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.ReleaseDate)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.ReleaseDate)
            @Html.ValidationMessageFor(Function(model) model.ReleaseDate)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.Genre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.Genre)
            @Html.ValidationMessageFor(Function(model) model.Genre)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.Price)
        </div>
        <div class="editor-field">
            @Html.EditorFor(Function(model) model.Price)
            @Html.ValidationMessageFor(Function(model) model.Price)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
End Using

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Обратите внимание, что шаблон представления содержит @ModelType MvcMovie.Models.Movie инструкцию в верхней части файла. Это указывает, что представление ожидает, что модель для шаблона представления будет типом Movie.

Шаблонный код использует несколько вспомогательных методов для упрощения разметки HTML. Вспомогателя Html.LabelFor отображается имя поля ("Title", "ReleaseDate", "Жанр" или "Цена"). Вспомогательный Html.EditorFor элемент отображает элемент HTML <input> . Вспомогательный Html.ValidationMessageFor элемент отображает все сообщения проверки, связанные с этим свойством.

Запустите приложение и перейдите по URL-адресу /Movies . Щелкните ссылку Edit (Изменить). Просмотрите исходный код страницы в окне браузера. HTML-код на странице выглядит следующим образом. (Разметка меню была исключена для ясности.)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Edit</title>
    <link href="/Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
    <script src="/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>
</head>
<body>
    <div class="page">
        <header>
            <div id="title">
                <h1>MVC Movie App</h1>
            </div>
           ...
        </header>
        <section id="main">

<h2>Edit</h2>

<script src="/Scripts/jquery.validate.min.js" type="text/javascript"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js" type="text/javascript"></script>

<form action="/Movies/Edit/4" method="post">    <fieldset>
        <legend>Movie</legend>

        <input data-val="true" data-val-number="The field ID must be a number." 
    data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" />

        <div class="editor-label">
            <label for="Title">Title</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" />
            <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="ReleaseDate">ReleaseDate</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-required="The ReleaseDate field is required." 
    id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" />
            <span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Genre">Genre</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" />
            <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span>
        </div>

        <div class="editor-label">
            <label for="Price">Price</label>
        </div>
        <div class="editor-field">
            <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." 
    data-val-required="The Price field is required." id="Price" name="Price" type="text" value="9.99" />
            <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span>
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
</form>
<div>
    <a href="/Movies">Back to List</a>
</div>

        </section>
        <footer>
        </footer>
    </div>
</body>
</html>

Элементы <input> находятся в html-элементе <form> , атрибут которого action задается для записи в URL-адрес /Movies/Edit . Данные формы будут размещены на сервере при нажатии кнопки "Изменить ".

Обработка запроса POST

В следующем листинге демонстрируется версия HttpPost метода действия Edit.

'
' POST: /Movies/Edit/5

<HttpPost()>
Function Edit(movie As Movie) As ActionResult
    If ModelState.IsValid Then
        db.Entry(movie).State = EntityState.Modified
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If

    Return View(movie)
End Function

Привязка модели ASP.NET платформы принимает опубликованные значения формы и создает Movie объект, переданный в качестве movie параметра. Проверка ModelState.IsValid кода проверяет, можно ли использовать данные, отправленные в форме, для изменения Movie объекта. Если данные допустимы, код сохраняет данные фильма в Movies коллекцию экземпляра MovieDBContext . Затем код сохраняет новые данные фильма в базу данных путем вызова SaveChanges метода MovieDBContext, который сохраняет изменения в базе данных. После сохранения данных код перенаправляет пользователя к Index методу MoviesController действия класса, что приводит к отображению обновленного фильма в списке фильмов.

Если опубликованные значения недопустимы, они переиграются в форме. Вспомогательные Html.ValidationMessageFor элементы в шаблоне представления Edit.vbhtml заботятся о отображении соответствующих сообщений об ошибках.

abcNotValid

Обратите внимание на языковые стандарта , если вы обычно работаете с языковым стандартом, отличном от английского языка, см . раздел "Поддержка ASP.NET MVC 3 с помощью языковых стандартов, отличных от английского языка".

Создание более надежного метода edit

Метод HttpGet Edit , созданный системой шаблонов, не проверяет, является ли идентификатор, переданный в него. Если пользователь удаляет сегмент идентификатора из URL-адреса (http://localhost:xxxxx/Movies/Edit), отображается следующая ошибка:

Null_ID

Пользователь также может передать идентификатор, который не существует в базе данных, например http://localhost:xxxxx/Movies/Edit/1234. Чтобы устранить это ограничение, можно внести два изменения в HttpGet Edit метод действия. Во-первых, измените ID параметр, указав значение по умолчанию нулевого значения, если идентификатор не передается явным образом. Вы также можете проверить, найден Find ли метод фильма перед возвратом объекта фильма в шаблон представления. Обновленный Edit метод показан ниже.

Public Function Edit(Optional ByVal id As Integer = 0) As ActionResult
    Dim movie As Movie = db.Movies.Find(id)
    If movie Is Nothing Then
        Return HttpNotFound()
    End If
    Return View(movie)
End Function

Если фильм не найден, HttpNotFound вызывается метод.

HttpGet Все методы соответствуют аналогичному шаблону. Они получают объект фильма (или список объектов в случае Index) и передают модель в представление. Метод Create передает пустой объект фильма в представление Create. Все методы, которые создают, редактируют, удаляют или иным образом изменяют данные, делают это в перегрузке метода HttpPost. Изменение данных в методе HTTP GET представляет собой риск безопасности. Изменение данных в методе GET также нарушает рекомендации ПО HTTP и шаблон REST архитектуры, который указывает, что запросы GET не должны изменять состояние приложения. Другими словами, выполнение операции GET должно быть безопасной операцией, которая не имеет побочных эффектов.

Добавление метода поиска и представления поиска

В этом разделе вы добавите SearchIndex метод действия, позволяющий искать фильмы по жанру или имени. Это будет доступно с помощью URL-адреса /Movies/SearchIndex . Запрос отобразит HTML-форму, содержащую входные элементы, которые пользователь может заполнить для поиска фильма. Когда пользователь отправляет форму, метод действия получит значения поиска, опубликованные пользователем, и используйте значения для поиска в базе данных.

SearchIndx_SM

Отображение формы SearchIndex

Начните с добавления метода действия в существующий SearchIndex MoviesController класс. Метод возвращает представление, содержащее HTML-форму. Вот этот код:

Public Function SearchIndex(ByVal searchString As String) As ActionResult
    Dim movies = From m In db.Movies
                 Select m 

    If Not String.IsNullOrEmpty(searchString) Then 
        movies = movies.Where(Function(s) s.Title.Contains(searchString)) 
    End If
    Return View(movies) 
End Function

Первая строка SearchIndex метода создает следующий запрос LINQ , чтобы выбрать фильмы:

Dim movies = From m In db.Movies    Select m

Запрос определен на этом этапе, но еще не был запущен в хранилище данных.

searchString Если параметр содержит строку, запрос фильмов изменяется для фильтрации по значению строки поиска, используя следующий код:

Значение If String.IsNullOrEmpty(searchString)
фильмы = фильмы. Where(Function(s) s.Title.Contains(searchString))
Конец if

Запросы LINQ не выполняются, когда они определены или изменяются путем вызова метода, Where например или OrderBy. Вместо этого выполнение запроса отложено, что означает, что оценка выражения задерживается до тех пор, пока не будет фактически итерировано значение или ToList вызывается метод. SearchIndex В примере запрос выполняется в представлении SearchIndex. Дополнительные сведения об отложенном и немедленном выполнении запросов см. в разделе Выполнение запроса.

Теперь вы можете реализовать SearchIndex представление, отображающее форму пользователю. Щелкните правой кнопкой мыши внутри SearchIndex метода и нажмите кнопку "Добавить представление". В диалоговом окне "Добавление представления" укажите, что объект будет передаваться Movie шаблону представления в качестве класса модели. В списке шаблонов шаблонов шаблонов выберите "Список", а затем нажмите кнопку "Добавить".

AddSearchView

При нажатии кнопки "Добавить" создается шаблон представления Views\Movies\SearchIndex.vbhtml. Так как вы выбрали список в списке шаблонов шаблонов, Visual Web Developer автоматически создает (шаблон) некоторые содержимое по умолчанию в представлении. Формирование шаблонов создало HTML-форму. Он изучил Movie класс и создал код для отрисовки <label> элементов для каждого свойства класса. В приведенном ниже списке показано представление "Создать", созданное:

@ModelType IEnumerable(Of MvcMovie.Movie)

@Code
    ViewData("Title") = "SearchIndex"
End Code

<h2>SearchIndex</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th>
            Title
        </th>
        <th>
            ReleaseDate
        </th>
        <th>
            Genre
        </th>
        <th>
            Price
        </th>
        <th></th>
    </tr>

@For Each item In Model
    Dim currentItem = item
    @<tr>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Title)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.ReleaseDate)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Genre)
        </td>
        <td>
            @Html.DisplayFor(Function(modelItem) currentItem.Price)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |
            @Html.ActionLink("Details", "Details", New With {.id = currentItem.ID}) |
            @Html.ActionLink("Delete", "Delete", New With {.id = currentItem.ID})
        </td>
    </tr>
Next

</table>

Запустите приложение и перейдите к /Movies/SearchIndex. Добавьте в URL-адрес строку запроса, например ?searchString=ghost. Отображаются отфильтрованные фильмы.

SearchQryStr

Если изменить сигнатуру SearchIndex метода с именем idпараметра, id параметр будет соответствовать {id} заполнителю маршрутов по умолчанию, заданным в файле Global.asax .

{controller}/{action}/{id}

Измененный SearchIndex метод будет выглядеть следующим образом:

Public Function SearchIndex(ByVal id As String) As ActionResult
Dim searchString As String = id
Dim movies = From m In db.Movies
             Select m

If Not String.IsNullOrEmpty(searchString) Then
    movies = movies.Where(Function(s) s.Title.Contains(searchString))
End If

Return View(movies)
End Function

Теперь можно передать заголовок поиска в качестве данных маршрута (сегмент URL-адреса) вместо значения строки запроса.

SearchRouteData

Тем не менее пользователи вряд ли будут каждый раз изменять URL-адрес для поиска фильмов. Теперь вы добавите пользовательский интерфейс, чтобы помочь им фильтровать фильмы. Если вы изменили сигнатуру метода, чтобы проверить, как передать параметр идентификатора SearchIndex , привязанного к маршруту, измените его обратно, чтобы SearchIndex метод принимает строковый параметр с именем searchString:

Откройте файл Views\Movies\SearchIndex.vbhtml и сразу после @Html.ActionLink("Create New", "Create")этого добавьте следующее:

@Code
    ViewData("Title") = "SearchIndex"
    Using (Html.BeginForm())
         @<p> Title: @Html.TextBox("SearchString") 
         <input type="submit" value="Filter" /></p>
        End Using
End Code

Вспомогательный Html.BeginForm элемент создает открывающий <form> тег. Вспомогательный Html.BeginForm элемент вызывает отправку формы, когда пользователь отправляет форму, нажав кнопку "Фильтр ".

Запустите приложение и попробуйте найти фильм.

SearchIndxIE9_title

Нет HttpPost перегрузки SearchIndex метода. Он не нужен, так как метод не изменяет состояние приложения, просто фильтруя данные. Если вы добавили следующий HttpPost SearchIndex метод, вызывающий действие будет соответствовать SearchIndex HttpPost методу, и HttpPost SearchIndex метод будет выполняться, как показано на рисунке ниже.

<HttpPost()>
 Public Function SearchIndex(ByVal fc As FormCollection, ByVal searchString As String) As String
     Return "<h3> From [HttpPost]SearchIndex: " & searchString & "</h3>"
 End Function

SearchPostGhost

Добавление поиска по жанру

Если вы добавили HttpPost версию SearchIndex метода, удалите его сейчас.

Затем вы добавите функцию, чтобы пользователи искали фильмы по жанру. Замените метод SearchIndex следующим кодом:

Public Function SearchIndex(ByVal movieGenre As String, ByVal searchString As String) As ActionResult
    Dim GenreLst = New List(Of String)()

    Dim GenreQry = From d In db.Movies
                   Order By d.Genre
                   Select d.Genre
    GenreLst.AddRange(GenreQry.Distinct())
    ViewBag.movieGenre = New SelectList(GenreLst)

    Dim movies = From m In db.Movies
                 Select m

    If Not String.IsNullOrEmpty(searchString) Then
        movies = movies.Where(Function(s) s.Title.Contains(searchString))
    End If

    If String.IsNullOrEmpty(movieGenre) Then
        Return View(movies)
    Else
        Return View(movies.Where(Function(x) x.Genre = movieGenre))
    End If

End Function

Эта версия метода принимает дополнительный SearchIndex параметр, а именно movieGenre. Первые несколько строк кода создают List объект для хранения жанров фильмов из базы данных.

Следующий код определяет запрос LINQ, который извлекает все жанры из базы данных.

Dim GenreQry = From d In db.Movies
                   Order By d.Genre
                   Select d.Genre

Код использует AddRange метод универсальной List коллекции для добавления всех различных жанров в список. (Без Distinct модификатора будут добавлены повторяющиеся жанры — например, комедия будет добавлена дважды в нашем примере). Затем код сохраняет список жанров в объекте ViewBag .

В следующем коде показано, как проверить movieGenre параметр. Если код не пуст, он дополнительно ограничивает запрос фильмов, чтобы ограничить выбранные фильмы указанным жанром.

If String.IsNullOrEmpty(movieGenre) Then
        Return View(movies)
    Else
        Return View(movies.Where(Function(x) x.Genre = movieGenre))
    End If

Добавление разметки в представление SearchIndex для поддержки поиска по жанру

Добавьте вспомогательный Html.DropDownList файл Views\Movies\SearchIndex.vbhtml перед вспомогательным файлом TextBox . Завершенная разметка показана ниже:

<p>
    @Html.ActionLink("Create New", "Create")
    @Code
    ViewData("Title") = "SearchIndex"
    Using (Html.BeginForm())
         @<p> Genre: @Html.DropDownList("movieGenre", "All")
         Title: @Html.TextBox("SearchString") 
         <input type="submit" value="Filter" /></p>
        End Using
End Code
</p>

Запустите приложение и перейдите к /Movies/SearchIndex. Попробуйте выполнить поиск по жанру, по имени фильма и по обоим критериям.

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