Изучение методов Edit и представления Edit (C#)
Примечание.
Обновленная версия этого руководства доступна здесь , где используется ASP.NET MVC 5 и Visual Studio 2013. Это более безопасно, гораздо проще следовать и демонстрирует больше функций.
В этом руководстве описаны основы создания веб-приложения 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 с исходным кодом C# доступен для сопровождения этого раздела. Скачайте версию C#. Если вы предпочитаете Visual Basic, перейдите на версию этого руководства visual Basic.
В этом разделе вы изучите созданные методы действий и представления для контроллера фильма. Затем вы добавите настраиваемую страницу поиска.
Запустите приложение и перейдите к Movies
контроллеру, добавив /Movies к URL-адресу в адресной строке браузера. Удерживайте указатель мыши на ссылку "Изменить ", чтобы просмотреть URL-адрес, на который он ссылается.
Ссылка "Изменить" была создана методом Html.ActionLink
в представлении Views\Movies\Index.cshtml:
@Html.ActionLink("Edit", "Edit", new { id=item.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
public ActionResult Edit(int id)
{
Movie movie = db.Movies.Find(id);
return View(movie);
}
//
// POST: /Movies/Edit/5
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Обратите внимание на второй метод действия Edit
, которому предшествует атрибут HttpPost
. Этот атрибут указывает, что перегрузка Edit
метода может вызываться только для запросов POST. Атрибут можно применить HttpGet
к первому методу редактирования, но это не обязательно, так как это значение по умолчанию. (Мы будем ссылаться на методы действий, неявно назначенные атрибуту HttpGet
в качестве HttpGet
методов.)
Метод HttpGet
Edit
принимает параметр идентификатора фильма, ищет фильм с помощью метода Entity Framework Find
и возвращает выбранный фильм в представление "Изменить". Если в представлении редактирования создана система формирования шаблонов, она проверяет класс Movie
и создает код для отображения элементов <label>
и <input>
для каждого свойства класса. В следующем примере показано представление "Изменить", созданное:
@model MvcMovie.Models.Movie
@{
ViewBag.Title = "Edit";
}
<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(model => model.ID)
<div class="editor-label">
@Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ReleaseDate)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ReleaseDate)
@Html.ValidationMessageFor(model => model.ReleaseDate)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Genre)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Genre)
@Html.ValidationMessageFor(model => model.Genre)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Price)
@Html.ValidationMessageFor(model => model.Price)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Обратите внимание, что шаблон представления содержит @model 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
.
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
Привязка модели ASP.NET платформы принимает опубликованные значения формы и создает Movie
объект, переданный в качестве movie
параметра. Проверка ModelState.IsValid
кода проверяет, можно ли использовать данные, отправленные в форме, для изменения Movie
объекта. Если данные допустимы, код сохраняет данные фильма в Movies
коллекцию экземпляра MovieDBContext
. Затем код сохраняет новые данные фильма в базу данных путем вызова SaveChanges
метода MovieDBContext
, который сохраняет изменения в базе данных. После сохранения данных код перенаправляет пользователя к Index
методу MoviesController
действия класса, что приводит к отображению обновленного фильма в списке фильмов.
Если опубликованные значения недопустимы, они переиграются в форме. Вспомогательные Html.ValidationMessageFor
элементы в шаблоне представления Edit.cshtml заботятся о отображении соответствующих сообщений об ошибках.
Обратите внимание на языковые стандарта , если вы обычно работаете с языковым стандартом, отличном от английского языка, см . раздел "Поддержка 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 ActionResult Edit(int id = 0)
{
Movie movie = db.Movies.Find(id);
if (movie == null)
{
return HttpNotFound();
}
return View(movie);
}
Если фильм не найден, HttpNotFound
вызывается метод.
HttpGet
Все методы соответствуют аналогичному шаблону. Они получают объект фильма (или список объектов в случае Index
) и передают модель в представление. Метод Create
передает пустой объект фильма в представление Create. Все методы, которые создают, редактируют, удаляют или иным образом изменяют данные, делают это в перегрузке метода HttpPost
. Изменение данных в методе HTTP GET представляет собой риск безопасности. Изменение данных в методе GET также нарушает рекомендации ПО HTTP и шаблон REST архитектуры, который указывает, что запросы GET не должны изменять состояние приложения. Другими словами, выполнение операции GET должно быть безопасной операцией, которая не имеет побочных эффектов.
Добавление метода поиска и представления поиска
В этом разделе вы добавите SearchIndex
метод действия, позволяющий искать фильмы по жанру или имени. Это будет доступно с помощью URL-адреса /Movies/SearchIndex . Запрос отобразит HTML-форму, содержащую входные элементы, которые пользователь может заполнить для поиска фильма. Когда пользователь отправляет форму, метод действия получит значения поиска, опубликованные пользователем, и используйте значения для поиска в базе данных.
Отображение формы SearchIndex
Начните с добавления метода действия в существующий SearchIndex
MoviesController
класс. Метод возвращает представление, содержащее HTML-форму. Вот этот код:
public ActionResult SearchIndex(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Первая строка SearchIndex
метода создает следующий запрос LINQ , чтобы выбрать фильмы:
var movies = from m in db.Movies
select m;
Запрос определен на этом этапе, но еще не был запущен в хранилище данных.
searchString
Если параметр содержит строку, запрос фильмов изменяется для фильтрации по значению строки поиска, используя следующий код:
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
Запросы LINQ не выполняются, когда они определены или изменяются путем вызова метода, Where
например или OrderBy
. Вместо этого выполнение запроса отложено, что означает, что оценка выражения задерживается до тех пор, пока не будет фактически итерировано значение или ToList
вызывается метод. SearchIndex
В примере запрос выполняется в представлении SearchIndex. Дополнительные сведения об отложенном и немедленном выполнении запросов см. в разделе Выполнение запроса.
Теперь вы можете реализовать SearchIndex
представление, отображающее форму пользователю. Щелкните правой кнопкой мыши внутри SearchIndex
метода и нажмите кнопку "Добавить представление". В диалоговом окне "Добавление представления" укажите, что объект будет передаваться Movie
шаблону представления в качестве класса модели. В списке шаблонов шаблонов шаблонов выберите "Список", а затем нажмите кнопку "Добавить".
При нажатии кнопки "Добавить" создается шаблон представления Views\Movies\SearchIndex.cshtml. Так как вы выбрали список в списке шаблонов шаблонов, Visual Web Developer автоматически создает (шаблон) некоторые содержимое по умолчанию в представлении. Формирование шаблонов создало HTML-форму. Он изучил Movie
класс и создал код для отрисовки <label>
элементов для каждого свойства класса. В приведенном ниже списке показано представление "Создать", созданное:
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "SearchIndex";
}
<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>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.ReleaseDate)
</td>
<td>
@Html.DisplayFor(modelItem => item.Genre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
@Html.ActionLink("Details", "Details", new { id=item.ID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.ID })
</td>
</tr>
}
</table>
Запустите приложение и перейдите к /Movies/SearchIndex. Добавьте в URL-адрес строку запроса, например ?searchString=ghost
. Отображаются отфильтрованные фильмы.
Если изменить сигнатуру SearchIndex
метода с именем id
параметра, id
параметр будет соответствовать {id}
заполнителю маршрутов по умолчанию, заданным в файле Global.asax .
{controller}/{action}/{id}
Измененный SearchIndex
метод будет выглядеть следующим образом:
public ActionResult SearchIndex(string id)
{
string searchString = id;
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Теперь можно передать заголовок поиска в качестве данных маршрута (сегмент URL-адреса) вместо значения строки запроса.
Тем не менее пользователи вряд ли будут каждый раз изменять URL-адрес для поиска фильмов. Теперь вы добавите пользовательский интерфейс, чтобы помочь им фильтровать фильмы. Если вы изменили сигнатуру метода, чтобы проверить, как передать параметр идентификатора SearchIndex
, привязанного к маршруту, измените его обратно, чтобы SearchIndex
метод принимает строковый параметр с именем searchString
:
public ActionResult SearchIndex(string searchString)
{
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
return View(movies);
}
Откройте файл Views\Movies\SearchIndex.cshtml и сразу после @Html.ActionLink("Create New", "Create")
этого добавьте следующее:
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" /></p>
}
В следующем примере показана часть файла Views\Movies\SearchIndex.cshtml с добавленной разметкой фильтрации.
@model IEnumerable<MvcMovie.Models.Movie>
@{
ViewBag.Title = "SearchIndex";
}
<h2>SearchIndex</h2>
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p> Title: @Html.TextBox("SearchString") <br />
<input type="submit" value="Filter" /></p>
}
</p>
Вспомогательный Html.BeginForm
элемент создает открывающий <form>
тег. Вспомогательный Html.BeginForm
элемент вызывает отправку формы, когда пользователь отправляет форму, нажав кнопку "Фильтр ".
Запустите приложение и попробуйте найти фильм.
Нет HttpPost
перегрузки SearchIndex
метода. Он не нужен, так как метод не изменяет состояние приложения, просто фильтруя данные.
Можно добавить следующий метод HttpPost SearchIndex
. В этом случае вызывающий объект действия будет соответствовать HttpPost SearchIndex
методу, и HttpPost SearchIndex
метод будет выполняться, как показано на рисунке ниже.
[HttpPost]
public string SearchIndex(FormCollection fc, string searchString)
{
return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>";
}
Тем не менее при добавлении этой версии HttpPost
метода SearchIndex
существует ограничение на общую реализацию. Допустим, вам необходимо добавить в закладки конкретный поиск или отправить друзьям ссылку, по которой они могут просмотреть аналогичный отфильтрованный список фильмов. Обратите внимание, что URL-адрес HTTP-запроса POST совпадает с URL-адресом запроса GET (localhost:xxxxx/Movies/SearchIndex) — в самом URL-адресе нет сведений об поиске. Сейчас данные строки поиска отправляются на сервер в качестве значения поля формы. Это означает, что вы не можете записывать данные поиска для закладки или отправки друзьям в URL-адресе.
Решение заключается в том, чтобы использовать перегрузку BeginForm
, указывающую, что запрос POST должен добавить данные поиска в URL-адрес, и он должен быть перенаправлен в версию SearchIndex
HttpGet метода. Замените существующий метод без BeginForm
параметров следующим образом:
@using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))
Теперь при отправке поиска URL-адрес содержит строку запроса поиска. Поиск также переносится в метод HttpGet SearchIndex
, даже если у вас определен метод HttpPost SearchIndex
.
Добавление поиска по жанру
Если вы добавили HttpPost
версию SearchIndex
метода, удалите его сейчас.
Затем вы добавите функцию, чтобы пользователи искали фильмы по жанру. Замените метод SearchIndex
следующим кодом:
public ActionResult SearchIndex(string movieGenre, string searchString)
{
var GenreLst = new List<string>();
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
GenreLst.AddRange(GenreQry.Distinct());
ViewBag.movieGenre = new SelectList(GenreLst);
var movies = from m in db.Movies
select m;
if (!String.IsNullOrEmpty(searchString))
{
movies = movies.Where(s => s.Title.Contains(searchString));
}
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
}
Эта версия метода принимает дополнительный SearchIndex
параметр, а именно movieGenre
. Первые несколько строк кода создают List
объект для хранения жанров фильмов из базы данных.
Следующий код определяет запрос LINQ, который извлекает все жанры из базы данных.
var GenreQry = from d in db.Movies
orderby d.Genre
select d.Genre;
Код использует AddRange
метод универсальной List
коллекции для добавления всех различных жанров в список. (Без Distinct
модификатора будут добавлены повторяющиеся жанры — например, комедия будет добавлена дважды в нашем примере). Затем код сохраняет список жанров в объекте ViewBag
.
В следующем коде показано, как проверить movieGenre
параметр. Если код не пуст, он дополнительно ограничивает запрос фильмов, чтобы ограничить выбранные фильмы указанным жанром.
if (string.IsNullOrEmpty(movieGenre))
return View(movies);
else
{
return View(movies.Where(x => x.Genre == movieGenre));
}
Добавление разметки в представление SearchIndex для поддержки поиска по жанру
Добавьте вспомогательный Html.DropDownList
элемент в файл Views\Movies\SearchIndex.cshtml перед вспомогательным файлом TextBox
. Завершенная разметка показана ниже:
<p>
@Html.ActionLink("Create New", "Create")
@using (Html.BeginForm()){
<p>Genre: @Html.DropDownList("movieGenre", "All")
Title: @Html.TextBox("SearchString")
<input type="submit" value="Filter" /></p>
}
</p>
Запустите приложение и перейдите к /Movies/SearchIndex. Попробуйте выполнить поиск по жанру, по имени фильма и по обоим критериям.
В этом разделе вы изучили методы и представления действий CRUD, созданные платформой. Вы создали метод действия поиска и представление, позволяющее пользователям выполнять поиск по названию фильма и жанру. В следующем разделе вы узнаете, как добавить свойство в Movie
модель и как добавить инициализатор, который автоматически создаст тестовую базу данных.