Examinar os métodos de edição e a exibição de edição (VB)

por Rick Anderson

Este tutorial ensinará as noções básicas da criação de um aplicativo Web ASP.NET MVC usando o Microsoft Visual Web Developer 2010 Express Service Pack 1, que é uma versão gratuita do Microsoft Visual Studio. Antes de começar, verifique se você instalou os pré-requisitos listados abaixo. Você pode instalar todos eles clicando no seguinte link: Web Platform Installer. Como alternativa, você pode instalar os pré-requisitos individualmente usando os seguintes links:

Se você estiver usando o Visual Studio 2010 em vez do Visual Web Developer 2010, instale os pré-requisitos clicando no seguinte link: pré-requisitos do Visual Studio 2010.

Um projeto do Visual Web Developer com código-fonte VB.NET está disponível para acompanhar este tópico. Baixe a versão do VB.net. Se você preferir o C#, mude para a versão c# deste tutorial.

Nesta seção, você examinará os métodos de ação e as exibições gerados para o controlador de filme. Em seguida, você adicionará uma página de pesquisa personalizada.

Execute o aplicativo e navegue até o Movies controlador acrescentando /Movies à URL na barra de endereços do seu navegador. Mantenha o ponteiro do mouse sobre um link de edição para ver a URL à qual ele se vincula.

EditLink_sm

O link de edição foi gerado pelo Html.ActionLink método na exibição Views\Movies\Index.vbhtml :

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

EditLink_sm

O Html objeto é um auxiliar que é exposto usando uma propriedade na WebViewPage classe base. O ActionLink método do auxiliar facilita a geração dinâmica de hiperlinks HTML que se vinculam a métodos de ação em controladores. O primeiro argumento para o ActionLink método é o texto do link a ser renderizado (por exemplo, <a>Edit Me</a> ). O segundo argumento é o nome do método de ação a ser invocado. O argumento final é um objeto anônimo que gera os dados da rota (nesse caso, a ID de 4).

O link gerado mostrado na imagem anterior é http://localhost:xxxxx/Movies/Edit/4 . A rota padrão usa o padrão de URL {controller}/{action}/{id} . Portanto, o ASP.NET se traduz http://localhost:xxxxx/Movies/Edit/4 em uma solicitação para o Edit método de ação do Movies controlador com o parâmetro ID igual a 4.

Você também pode passar parâmetros de método de ação usando uma cadeia de caracteres de consulta. Por exemplo, a URL http://localhost:xxxxx/Movies/Edit?ID=4 também passa o parâmetro ID de 4 para o Edit método de ação do Movies controlador.

EditQueryString

Abra o Movies controlador. Os dois Edit métodos de ação são mostrados abaixo.

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

Observe se o segundo método de ação Edit é precedido pelo atributo HttpPost. Esse atributo especifica que a sobrecarga do Edit método pode ser invocada somente para solicitações post. Você pode aplicar o HttpGet atributo ao primeiro método de edição, mas isso não é necessário porque é o padrão. (Vamos nos referir aos métodos de ação que são atribuídos implicitamente ao HttpGet atributo como HttpGet métodos.)

O HttpGet Edit método usa o parâmetro ID do filme, pesquisa o filme usando o Find Método Entity Framework e retorna o filme selecionado para o modo de exibição Editar. Quando o sistema de scaffolding criou a exibição de Edição, ele examinou a classe Movie e o código criado para renderizar os elementos <label> e <input> de cada propriedade da classe. O exemplo a seguir mostra a exibição de edição que foi gerada:

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

Observe como o modelo de exibição tem uma @ModelType MvcMovie.Models.Movie instrução na parte superior do arquivo — isso especifica que a exibição espera que o modelo para o modelo de exibição seja do tipo Movie .

O código com Scaffold usa vários métodos auxiliares para simplificar a marcação HTML. O Html.LabelFor auxiliar exibe o nome do campo ( " título " , " liberado " , " gênero " ou " preço " ). O Html.EditorFor auxiliar exibe um <input> elemento HTML. O Html.ValidationMessageFor auxiliar exibe todas as mensagens de validação associadas a essa propriedade.

Execute o aplicativo e navegue até a URL /Movies . Clique em um link de edição . No navegador, exiba a origem da página. O HTML na página é semelhante ao exemplo a seguir. (A marcação do menu foi excluída para fins de clareza.)

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

Os <input> elementos estão em um <form> elemento HTML cujo action atributo está definido como post para a URL /Movies/Edit . Os dados do formulário serão postados no servidor quando o botão Editar for clicado.

Processando a solicitação POST

A lista a seguir mostra a versão HttpPost do método de ação 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

O associador de modelo do ASP.NET Framework pega os valores de formulário postados e cria um Movie objeto que é passado como o movie parâmetro. O ModelState.IsValid check-in do código verifica se os dados enviados no formulário podem ser usados para modificar um Movie objeto. Se os dados forem válidos, o código salvará os dados do filme na Movies coleção da MovieDBContext instância. Em seguida, o código salva os novos dados do filme no banco de dado chamando o SaveChanges método de MovieDBContext , que persiste as alterações no banco de dados. Depois de salvar os dados, o código redireciona o usuário para o Index método de ação da MoviesController classe, o que faz com que o filme atualizado seja exibido na lista de filmes.

Se os valores postados não forem válidos, eles serão exibidos novamente no formulário. Os Html.ValidationMessageFor auxiliares no modelo de exibição Edit. vbhtml cuidam da exibição das mensagens de erro apropriadas.

abcNotValid

Observação sobre localidades Se você normalmente trabalha com uma localidade diferente do inglês, consulte dando suporte à validação do ASP.NET MVC 3 com localidades não inglesas.

Tornando o método de edição mais robusto

O HttpGet Edit método gerado pelo sistema scaffolding não verifica se a ID passada para ele é válida. Se um usuário remover o segmento de ID da URL ( http://localhost:xxxxx/Movies/Edit ), o seguinte erro será exibido:

Null_ID

Um usuário também pode passar uma ID que não existe no banco de dados, como http://localhost:xxxxx/Movies/Edit/1234 . Você pode fazer duas alterações no HttpGet Edit método de ação para resolver essa limitação. Primeiro, altere o ID parâmetro para ter um valor padrão de zero quando uma ID não for explicitamente passada. Você também pode verificar se o Find método realmente encontrou um filme antes de retornar o objeto de filme para o modelo de exibição. O Edit método atualizado é mostrado abaixo.

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

Se nenhum filme for encontrado, o HttpNotFound método será chamado.

Todos os HttpGet métodos seguem um padrão semelhante. Eles obtêm um objeto de filme (ou lista de objetos, no caso de Index ) e passam o modelo para a exibição. O Create método passa um objeto de filme vazio para o modo de exibição de criação. Todos os métodos que criam, editam, excluem ou, de outro modo, modificam dados fazem isso na sobrecarga HttpPost do método. A modificação de dados em um método HTTP GET é um risco de segurança, conforme descrito na entrada de postagem de blog ASP.net a Tip do MVC #46 – não use os links de exclusão porque eles criam brechas de segurança. Modificar dados em um método GET também viola as práticas recomendadas de HTTP e o padrão de REST arquitetônico, que especifica que as solicitações GET não devem alterar o estado do seu aplicativo. Em outras palavras, a execução de uma operação GET deve ser uma operação segura que não tenha efeitos colaterais.

Adicionando um método de pesquisa e uma exibição de pesquisa

Nesta seção, você adicionará um SearchIndex método de ação que permite pesquisar filmes por gênero ou por nome. Isso estará disponível usando a URL /Movies/SearchIndex . A solicitação exibirá um formulário HTML que contém os elementos de entrada que um usuário pode preencher para pesquisar um filme. Quando um usuário envia o formulário, o método de ação Obtém os valores de pesquisa postados pelo usuário e usa os valores para pesquisar o banco de dados.

SearchIndx_SM

Exibindo o formulário SearchIndex

Comece adicionando um SearchIndex método de ação à classe existente MoviesController . O método retornará uma exibição que contém um formulário HTML. O código é o seguinte:

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

A primeira linha do SearchIndex método cria a seguinte consulta LINQ para selecionar os filmes:

Dim movies = From m In db.Movies    Select m

A consulta é definida neste ponto, mas ainda não foi executada no armazenamento de dados.

Se o searchString parâmetro contiver uma cadeia de caracteres, a consulta de filmes será modificada para filtrar o valor da cadeia de caracteres de pesquisa, usando o seguinte código:

Se não for String. IsNullOrEmpty (searchstring),
Filmes = filmes. Onde (função (s) s. título. contém (searchstring))
Terminar se

Consultas LINQ não são executadas quando são definidas ou quando são modificadas chamando um método como Where ou OrderBy . Em vez disso, a execução da consulta é adiada, o que significa que a avaliação de uma expressão é adiada até que seu valor percebido seja realmente iterado ou o ToList método seja chamado. No SearchIndex exemplo, a consulta é executada na exibição SearchIndex. Para obter mais informações sobre a execução de consulta adiada, consulte Execução da consulta.

Agora você pode implementar a SearchIndex exibição que exibirá o formulário para o usuário. Clique com o botão direito do mouse dentro do SearchIndex método e clique em Adicionar exibição. Na caixa de diálogo Adicionar exibição , especifique que você vai passar um Movie objeto para o modelo de exibição como sua classe de modelo. Na lista modelo de Scaffold , escolha listae clique em Adicionar.

AddSearchView

Quando você clica no botão Adicionar , o modelo de exibição Views\Movies\SearchIndex.vbhtml é criado. Como você selecionou lista na lista de modelos Scaffold , o Visual Web Developer gerou automaticamente (com scaffold) algum conteúdo padrão na exibição. O scaffolding criou um formulário HTML. Ele examinou a Movie classe e criou o código para renderizar <label> elementos para cada propriedade da classe. A listagem a seguir mostra o modo de exibição de criação que foi gerado:

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

Execute o aplicativo e navegue até /Movies/SearchIndex. Acrescente uma cadeia de consulta, como ?searchString=ghost, à URL. Os filmes filtrados são exibidos.

SearchQryStr

Se você alterar a assinatura do SearchIndex método para ter um parâmetro chamado id , o id parâmetro corresponderá ao {id} espaço reservado para as rotas padrão definidas no arquivo global. asax .

{controller}/{action}/{id}

O SearchIndex método modificado seria semelhante ao seguinte:

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

Agora você pode passar o título de pesquisa como dados de rota (um segmento de URL), em vez de como um valor de cadeia de consulta.

SearchRouteData

No entanto, você não pode esperar que os usuários modifiquem a URL sempre que desejarem pesquisar um filme. Agora, você adicionará a interface do usuário para ajudá-lo a filtrar filmes. Se você alterou a assinatura do SearchIndex método para testar como passar o parâmetro de ID de associação de rota, altere-o de volta para que o SearchIndex método aceite um parâmetro de cadeia de caracteres chamado searchString :

Abra o arquivo Views\Movies\SearchIndex.vbhtml e, logo após @Html.ActionLink("Create New", "Create") , adicione o seguinte:

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

O Html.BeginForm auxiliar cria uma marca de abertura <form> . O Html.BeginForm auxiliar faz com que o formulário seja Postado para si mesmo quando o usuário envia o formulário clicando no botão de filtro .

Execute o aplicativo e tente pesquisar um filme.

SearchIndxIE9_title

Não há HttpPost sobrecarga do SearchIndex método. Você não precisa dele, porque o método não está alterando o estado do aplicativo, apenas filtrando os dados. Se você adicionou o método a seguir HttpPost SearchIndex , o chamador de ação corresponderia ao HttpPost SearchIndex método e o HttpPost SearchIndex método seria executado conforme mostrado na imagem abaixo.

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

SearchPostGhost

Adicionando pesquisa por gênero

Se você adicionou a HttpPost versão do SearchIndex método, exclua-a agora.

Em seguida, você adicionará um recurso para permitir que os usuários pesquisem filmes por gênero. Substitua o método SearchIndex pelo seguinte código:

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

Essa versão do SearchIndex método usa um parâmetro adicional, ou seja, movieGenre As primeiras linhas de código criam um List objeto para conter gêneros de filme do banco de dados.

O código a seguir é uma consulta LINQ que recupera todos os gêneros do banco de dados.

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

O código usa o AddRange método da coleção genérica List para adicionar todos os gêneros distintos à lista. (Sem o Distinct modificador, os gêneros duplicados seriam adicionados; por exemplo, comédia seria adicionado duas vezes em nosso exemplo). Em seguida, o código armazena a lista de gêneros no ViewBag objeto.

O código a seguir mostra como verificar o movieGenre parâmetro. Se não estiver vazio, o código restringe ainda mais a consulta de filmes para limitar os filmes selecionados ao gênero especificado.

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

Adicionando marcação à exibição SearchIndex para dar suporte à pesquisa por gênero

Adicione um Html.DropDownList auxiliar ao arquivo Views\Movies\SearchIndex.vbhtml , logo antes do TextBox auxiliar. A marcação concluída é mostrada abaixo:

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

Execute o aplicativo e navegue até /Movies/SearchIndex. Experimente uma pesquisa por gênero, por nome de filme e por ambos os critérios.

Nesta seção, você examinou os métodos de ação CRUD e as exibições geradas pela estrutura. Você criou um método de ação de pesquisa e uma exibição que permitem que os usuários pesquisem por título e gênero do filme. Na próxima seção, você examinará como adicionar uma propriedade ao Movie modelo e como adicionar um inicializador que criará automaticamente um banco de dados de teste.