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

по Рик Андерсон (

В этом учебнике вы узнаете об основах создания веб-приложения 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-адрес в адресной строке браузера. Наведите указатель мыши на ссылку Edit (изменить ), чтобы просмотреть URL-адрес, на который он ссылается.

EditLink_sm

Ссылка для редактирования была создана Html.ActionLink методом в представлении виевс\мовиес\индекс.вбхтмл :

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

EditLink_sm

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

Созданная ссылка показана на предыдущем рисунке http://localhost:xxxxx/Movies/Edit/4 . Маршрут по умолчанию принимает шаблон URL-адреса {controller}/{action}/{id} . Таким образом, 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 к первому методу Edit, но это не обязательно, так как это значение по умолчанию. (Мы будем называть методы действий, которым неявно назначается HttpGet атрибут в качестве HttpGet методов.)

HttpGet Edit Метод принимает параметр идентификатора фильма, находит фильм с помощью Find метода Entity Framework и возвращает выбранный фильм в представление редактирования. Если в представлении редактирования создана система формирования шаблонов, она проверяет класс 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 " , " Жанр " или " Price " ). В 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 имеет значение POST для URL-адреса /мовиес/едит . Данные формы будут отправлены на сервер при нажатии кнопки " изменить ".

Обработка запроса 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 Framework принимает значения отправленной формы и создает 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 ), отображается следующая ошибка:

Null_ID

Пользователь также может передать идентификатор, который не существует в базе данных, например http://localhost:xxxxx/Movies/Edit/1234 . Чтобы устранить это ограничение, можно внести два изменения в HttpGet Edit метод действия. Во-первых, измените ID параметр, чтобы значение по умолчанию было равно нулю, если идентификатор не был явно передан. Кроме того, можно проверить, что Find метод обнаружил фильм перед возвратом объекта Movie в шаблон представления. Обновленный 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 методы соответствуют аналогичному шаблону. Они получают объект Movie (или список объектов, в случае использования Index ) и передают модель в представление. CreateМетод передает пустой объект Movie в представление создания. Все методы, которые создают, редактируют, удаляют или иным образом изменяют данные, делают это в перегрузке метода HttpPost. Изменение данных в методе HTTP GET является угрозой безопасности, как описано в записи блога совет ASP.NET #46 — не использовать ссылки DELETE, так как они создают бреши в системе безопасности. Изменение данных в методе GET также нарушает рекомендации по протоколу HTTP и шаблон архитектуры RESTFUL, указывающий, что запросы GET не должны изменять состояние приложения. Иными словами, выполнение операции GET должно быть безрезультатной операцией без побочных эффектов.

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

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

SearchIndx_SM

Отображение формы Сеарчиндекс

Начните с добавления 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 параметр содержит строку, запрос фильмов изменяется для фильтрации по значению строки поиска с помощью следующего кода:

Если не String. IsNullOrEmpty (searchString), то
Фильмы — Фильмы. WHERE (функции s. Title. Contains (searchString))
Окончание, если

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

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

аддсеарчвиев

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

@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>

Запустите приложение и перейдите по адресу /мовиес/сеарчиндекс. Добавьте в 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 :

Откройте файл виевс\мовиес\сеарчиндекс.вбхтмл и сразу после @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 метод, вызывающий объект действия будет соответствовать HttpPost SearchIndex методу, и 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

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

Добавьте Html.DropDownList вспомогательную функцию в файл виевс\мовиес\сеарчиндекс.вбхтмл непосредственно перед 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>

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

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