Изучение методов Edit и представления Edit (VB)
В этом руководстве описаны основы создания веб-приложения MVC ASP.NET MVC с помощью Microsoft Visual Web Developer 2010 Express с пакетом обновления 1 (SP1), который является бесплатной версией Microsoft Visual Studio. Перед началом работы убедитесь, что вы установили необходимые компоненты, перечисленные ниже. Все их можно установить, щелкнув следующую ссылку: установщик веб-платформы. Кроме того, можно установить предварительные требования по отдельности, используя следующие ссылки:
- Предварительные требования для Visual Studio Web Developer Express с пакетом обновления 1 (SP1)
- обновление средств MVC 3 ASP.NET
- SQL Server Compact 4.0(среда выполнения и средства поддержки)
Если вы используете Visual Studio 2010 вместо Visual Web Developer 2010, установите необходимые компоненты, щелкнув следующую ссылку: предварительные требования Visual Studio 2010.
Проект Visual Web Developer с VB.NET исходный код доступен для сопровождения этого раздела. Скачайте версию VB.NET. Если вы предпочитаете C#, перейдите к версии C# этого руководства.
В этом разделе вы изучите созданные методы действий и представления для контроллера фильма. Затем вы добавите настраиваемую страницу поиска.
Запустите приложение и перейдите к Movies
контроллеру, добавив /Movies к URL-адресу в адресной строке браузера. Удерживайте указатель мыши на ссылку "Изменить ", чтобы просмотреть URL-адрес, на который он ссылается.
Ссылка "Изменить" была создана методом Html.ActionLink
в представлении Views\Movies\Index.vbhtml:
@Html.ActionLink("Edit", "Edit", New With {.id = currentItem.ID}) |
Объект 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
.
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 заботятся о отображении соответствующих сообщений об ошибках.
Обратите внимание на языковые стандарта , если вы обычно работаете с языковым стандартом, отличном от английского языка, см . раздел "Поддержка ASP.NET MVC 3 с помощью языковых стандартов, отличных от английского языка".
Создание более надежного метода edit
Метод HttpGet
Edit
, созданный системой шаблонов, не проверяет, является ли идентификатор, переданный в него. Если пользователь удаляет сегмент идентификатора из URL-адреса (http://localhost:xxxxx/Movies/Edit
), отображается следующая ошибка:
Пользователь также может передать идентификатор, который не существует в базе данных, например 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-форму, содержащую входные элементы, которые пользователь может заполнить для поиска фильма. Когда пользователь отправляет форму, метод действия получит значения поиска, опубликованные пользователем, и используйте значения для поиска в базе данных.
Отображение формы 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
шаблону представления в качестве класса модели. В списке шаблонов шаблонов шаблонов выберите "Список", а затем нажмите кнопку "Добавить".
При нажатии кнопки "Добавить" создается шаблон представления 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
. Отображаются отфильтрованные фильмы.
Если изменить сигнатуру 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-адреса) вместо значения строки запроса.
Тем не менее пользователи вряд ли будут каждый раз изменять 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
элемент вызывает отправку формы, когда пользователь отправляет форму, нажав кнопку "Фильтр ".
Запустите приложение и попробуйте найти фильм.
Нет 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
Добавление поиска по жанру
Если вы добавили 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
модель и как добавить инициализатор, который автоматически создаст тестовую базу данных.