Apresentando ASP.NET Páginas Web - Atualização dos Dados da Base de Dados

por Tom FitzMacken

Este tutorial mostra-lhe como atualizar (alterar) uma entrada existente na base de dados quando utiliza ASP.NET Web Pages (Razor). Pressupõe-se que completou a série na Introdução de Dados Utilizando Formulários e ASP.NET em Páginas Web.

O que você vai aprender:

  • Como selecionar um registo individual no módulo auxiliar WebGrid.
  • Como ler um único registo de uma base de dados.
  • Como pré-carregar um formulário com valores do registo da base de dados.
  • Como atualizar um registo existente numa base de dados.
  • Como armazenar informação na página sem a exibir.
  • Como usar um campo oculto para armazenar informação.

Características/tecnologias discutidas:

  • O WebGrid ajudante.
  • O comando SQL Update .
  • O método Database.Execute.
  • Campos ocultos (<input type="hidden">).

O que você vai construir

No tutorial anterior, aprendeste a adicionar um registo a uma base de dados. Aqui, vais aprender a mostrar um registo para edição. Na página de Filmes, você atualizará o WebGrid assistente para que mostre um link Editar ao lado de cada filme:

Exibição WebGrid incluindo um link

Quando clicas no link Editar , leva-te a uma página diferente, onde a informação do filme já está numa forma:

Página Editar Filme mostrando filme a ser editado

Podes mudar qualquer um dos valores. Quando submetes as alterações, o código na página atualiza a base de dados e leva-te de volta à lista de filmes.

Esta parte do processo funciona quase exatamente como a página AddMovie.cshtml que criaste no tutorial anterior, por isso grande parte deste tutorial será familiar.

Existem várias formas de implementar uma forma de editar um filme individual. A abordagem apresentada foi escolhida porque é fácil de implementar e de compreender.

Para começar, irá atualizar a página de Filmes para que cada lista de filmes também inclua um link para Editar .

Abre o ficheiro Movies.cshtml .

No corpo da página, altere a WebGrid marcação adicionando uma coluna. Aqui está a marcação modificada.

@grid.GetHtml(
    tableStyle: "grid",
    headerStyle: "head",
    alternatingRowStyle: "alt",
    columns: grid.Columns(
        grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),
        grid.Column("Title"),
        grid.Column("Genre"),
        grid.Column("Year")
    )
)

A nova coluna é esta:

grid.Column(format: @<a href="~/EditMovie?id=@item.ID)">Edit</a>)

O objetivo desta coluna é mostrar um link (<a> elemento) cujo texto diz "Editar". O que queremos é criar um link que se pareça com o seguinte quando a página corre, com o id valor diferente para cada filme:

http://localhost:43097/EditMovie?id=7

Este link invocará uma página chamada EditMovie e passará a string ?id=7 de consulta para essa página.

A sintaxe da nova coluna pode parecer um pouco complexa, mas isso só porque junta vários elementos. Cada elemento individual é simples. Se se concentrar apenas no <a> elemento, verá esta marcação:

<a href="~/EditMovie?id=@item.ID)">Edit</a>

Um pouco de contexto sobre o funcionamento da grelha: a grelha mostra linhas, uma para cada registo da base de dados, e mostra colunas para cada campo no registo da base de dados. Enquanto cada linha da grelha está a ser construída, o item objeto contém o registo da base de dados (item) dessa linha. Este arranjo dá-te uma forma, no código, de aceder aos dados dessa linha. É isso que vês aqui: a expressão item.ID está a receber o valor ID do item atual da base de dados. Pode obter qualquer valor da base de dados (título, género ou ano) da mesma forma, usando item.Title, item.Genre, ou item.Year.

A expressão "~/EditMovie?id=@item.ID combina a parte codificada diretamente da URL de destino (~/EditMovie?id=) com este ID derivado dinamicamente. (Viste o ~ operador no tutorial anterior; é um operador ASP.NET que representa a raiz atual do site.)

O resultado é que esta parte da marcação na coluna gera algo semelhante à seguinte marcação em tempo de execução:

href="/EditMovie?id=2"

Naturalmente, o valor real de id será diferente para cada linha.

Criação de um Ecrã Personalizado para uma Coluna de Grelha

Agora, voltando à coluna da grelha. As três colunas que tinhas originalmente na grelha mostravam apenas os valores dos dados (título, género e ano). Especificou esta visualização passando o nome da coluna da base de dados — por exemplo, grid.Column("Title").

Esta nova coluna de links Editar é diferente. Em vez de especificar o nome da coluna, está a passar um format parâmetro. Este parâmetro permite-lhe definir a marcação que o WebGrid ajudante irá renderizar juntamente com o item valor para mostrar os dados da coluna em negrito, verde ou no formato que desejar. Por exemplo, se quiser que o título apareça a negrito, pode criar uma coluna como este exemplo:

grid.Column(format:@<strong>@item.Title</strong>)

(Os vários @ caracteres que vê na format propriedade marcam a transição entre marcação e um valor de código.)

Depois de conhecer a format propriedade, é mais fácil perceber como está organizada a nova coluna de links Editar :

grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),

A coluna consiste apenas na marcação que renderiza a ligação, mais alguma informação (o ID) extraída do registo da base de dados da linha.

Sugestão

Parâmetros nomeados e parâmetros posicionais para um método

Muitas vezes, quando chamou um método e lhe passou parâmetros, simplesmente listou os valores dos parâmetros separados por vírgulas. Eis alguns exemplos:

db.Execute(insertCommand, title, genre, year)

Validation.RequireField("title", "You must enter a title")

Não mencionámos o problema quando viste este código pela primeira vez, mas em cada caso, estás a passar parâmetros aos métodos numa ordem específica — ou seja, a ordem em que os parâmetros são definidos nesse método. Para db.Execute e Validation.RequireFields, se confundires a ordem dos valores que passas, recebes uma mensagem de erro quando a página corre, ou pelo menos alguns resultados estranhos. Claramente, é preciso saber a ordem para passar os parâmetros. (No WebMatrix, o IntelliSense pode ajudar-te a aprender a perceber o nome, tipo e ordem dos parâmetros.)

Como alternativa a passar os valores por ordem, pode usar parâmetros nomeados. (Passar parâmetros por ordem é conhecido como usar parâmetros posicionais.) Para parâmetros nomeados, incluis explicitamente o nome do parâmetro ao passar o seu valor. Já usaste parâmetros nomeados várias vezes nestes tutoriais. Por exemplo:

var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3)

e ainda

@grid.GetHtml(
    tableStyle: "grid",
    headerStyle: "head",
    alternatingRowStyle: "alt",
    columns: grid.Columns(
       grid.Column("Title"),
       grid.Column("Genre"),
       grid.Column("Year")
    )
)

Parâmetros nomeados são úteis em algumas situações, especialmente quando um método utiliza muitos parâmetros. Uma é quando queres passar apenas um ou dois parâmetros, mas os valores que queres passar não estão entre as primeiras posições na lista de parâmetros. Outra situação é quando queres tornar o teu código mais legível, passando os parâmetros na ordem que faz mais sentido para ti.

Obviamente, para usar parâmetros nomeados, é preciso conhecer os nomes dos parâmetros. O WebMatrix IntelliSense pode mostrar-lhe os nomes, mas atualmente não pode preenchê-los por si.

Criação da Página de Edição

Agora podes criar a página EditMovie . Quando os utilizadores clicam no link Editar , acabam por aparecer nesta página.

Crie uma página chamada EditMovie.cshtml e substitua o que está no ficheiro pela seguinte marcação:

<!DOCTYPE html>
<html>
  <head>
   <meta charset="utf-8" />
   <title>Edit a Movie</title>
    <style>
      .validation-summary-errors{
        border:2px dashed red;
        color:red;
        font-weight:bold;
        margin:12px;
      }
    </style>
  </head>
  <body>
    <h1>Edit a Movie</h1>
    @Html.ValidationSummary()
    <form method="post">
      <fieldset>
        <legend>Movie Information</legend>

        <p><label for="title">Title:</label>
           <input type="text" name="title" value="@title" /></p>

        <p><label for="genre">Genre:</label>
           <input type="text" name="genre" value="@genre" /></p>

        <p><label for="year">Year:</label>
           <input type="text" name="year" value="@year" /></p>

        <input type="hidden" name="movieid" value="@movieId" />

        <p><input type="submit" name="buttonSubmit" value="Submit Changes" /></p>
      </fieldset>
    </form>
  </body>
</html>

Esta marcação e código são semelhantes ao que tem na página AddMovie. Há uma pequena diferença no texto do botão de submeter. Tal como na página AddMovie, há uma Html.ValidationSummary chamada que mostra erros de validação, caso existam. Desta vez, estamos a deixar de fora as chamadas para Validation.Message, pois os erros serão exibidos no resumo de validação. Como referido no tutorial anterior, pode usar o resumo de validação e as mensagens de erro individuais em várias combinações.

Note novamente que o method atributo do <form> elemento é definido como post. Tal como na página AddMovie.cshtml , esta página faz alterações à base de dados. Portanto, este formulário deve realizar uma POST operação. (Para saber mais sobre a diferença entre as operações GET e POST, consulte a seção Segurança de GET, POST e Verbos HTTP no tutorial sobre formulários HTML.)

Como viste num tutorial anterior, os value atributos das caixas de texto estão a ser definidos com código Razor para os pré-carregar. Desta vez, no entanto, está a usar variáveis como title e genre para essa tarefa em vez de Request.Form["title"]:

<input type="text" name="title" value="@title" />

Como antes, esta marcação vai pré-carregar os valores das caixas de texto com os valores do filme. Vai perceber daqui a pouco porque é útil usar variáveis desta vez em vez de usar o objeto Request .

Há também um <input type="hidden"> elemento nesta página. Este elemento armazena o ID do filme sem o tornar visível na página. O ID é inicialmente passado para a página usando um valor de string de consulta (?id=7 ou semelhante na URL). Ao colocar o valor do ID num campo oculto, pode garantir que está disponível quando o formulário for submetido, mesmo que já não tenha acesso ao URL original com que a página foi invocada.

Ao contrário da página AddMovie , o código para a página EditMovie tem duas funções distintas. A primeira função é que, quando a página é exibida pela primeira vez (e então), o código recebe o ID do filme a partir da cadeia de consulta. O código usa então o ID para ler o filme correspondente da base de dados e exibi-lo (pré-carregar) nas caixas de texto.

A segunda função é que, quando o utilizador clica no botão Enviar Alterações , o código tem de ler os valores das caixas de texto e validá-los. O código também tem de atualizar o item da base de dados com os novos valores. Esta técnica é semelhante a adicionar um registo, como viste no AddMovie.

Adicionar Código para Ler um Único Filme

Para realizar a primeira função, adicione este código no topo da página:

@{
    var title = "";
    var genre = "";
    var year = "";
    var movieId = "";

    if(!IsPost){
        if(!Request.QueryString["ID"].IsEmpty()){
            movieId = Request.QueryString["ID"];
            var db = Database.Open("WebPagesMovies");
            var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
            var row = db.QuerySingle(dbCommand, movieId);
            title = row.Title;
            genre = row.Genre;
            year = row.Year;
        }
        else{
            Validation.AddFormError("No movie was selected.");
        }
    }
}

A maior parte deste código está dentro de um bloco que começa if(!IsPost). O ! operador significa "não", ou seja, a expressão indica se este pedido não for uma submissão de publicação, o que é uma forma indireta de dizer se este pedido é a primeira vez que esta página é executada. Como referido anteriormente, este código deve correr apenas na primeira execução da página. Se não incluires o código em if(!IsPost), ele seria executado sempre que a página fosse invocada, seja pela primeira vez ou em resposta a um clique de botão.

Repare que o código inclui um else bloco desta vez. Como dissemos quando introduzimos if blocos, por vezes queres correr código alternativo se a condição que estás a testar não for verdadeira. É o caso aqui. Se a condição for aprovada (isto é, se o ID passado para a página estiver aceitável), lês uma linha da base de dados. No entanto, se a condição não for aprovada, o else bloco é executado e o código cria uma mensagem de erro.

Validação de um valor passado para a página

O código usa Request.QueryString["id"] para obter o ID que é passado para a página. O código garante que um valor foi realmente passado para o ID. Se nenhum valor for passado, o código cria um erro de validação.

Este código mostra uma forma diferente de validar informação. No tutorial anterior, trabalhaste com o Validation ajudante. Registaste campos para validar e ASP.NET automaticamente fez a validação e mostrou erros usando Html.ValidationMessage e Html.ValidationSummary. Neste caso, no entanto, não estás realmente a validar a entrada do utilizador. Em vez disso, está a validar um valor que foi passado para a página a partir de outro lugar. O Validation ajudante não faz isso por ti.

Portanto, verifica-se o valor por si mesmo, testando-o com if(!Request.QueryString["ID"].IsEmpty()). Se houver algum problema, pode mostrar o erro usando Html.ValidationSummary, como fez com o Validation ajudante. Para isso, ligas Validation.AddFormError e passas-lhe uma mensagem para mostrar. Validation.AddFormError é um método incorporado que lhe permite definir mensagens personalizadas que se ligam ao sistema de validação com o qual já está familiarizado. (Mais adiante neste tutorial, vamos falar sobre como tornar este processo de validação um pouco mais robusto.)

Depois de garantir que existe um ID para o filme, o código lê a base de dados, procurando apenas um único item da base de dados. (Provavelmente já reparou no padrão geral para operações de base de dados: abrir a base de dados, definir uma instrução SQL e executar a instrução.) Desta vez, a instrução SQL Select inclui WHERE ID = @0. Como o ID é único, apenas um registo pode ser devolvido.

A consulta é feita usando db.QuerySingle (não db.Query, como usaste para a lista de filmes), e o código coloca o resultado na row variável. O nome row é arbitrário; podes nomear as variáveis como quiseres. As variáveis inicializadas no topo são então preenchidas com os detalhes do filme para que estes valores possam ser exibidos nas caixas de texto.

Testando a Página de Edição (até agora)

Se quiseres testar a tua página, abre já a página de Filmes e clica num link de Editar ao lado de qualquer filme. Verá a página do EditMovie com os detalhes preenchidos do filme que selecionou:

A captura de ecrã mostra a página Editar Filme onde se pode editar o filme.

Repara que o URL da página inclui algo como ?id=10 (ou outro número). Até agora testaste que os links de Editar na página de Filme funcionam, que a tua página está a ler o ID da string de consulta e que a consulta à base de dados para obter um único registo de filme está a funcionar.

Podes alterar a informação do filme, mas nada acontece quando clicas em Enviar Alterações.

Adicionar Código para Atualizar o Filme com as Alterações do Utilizador

No ficheiro EditMovie.cshtml, para implementar a segunda função (guardar alterações), adicione o seguinte código logo após a chave de encerramento do bloco @. (Se não tiver a certeza exatamente onde colocar o código, pode consultar a lista completa de código da página Editar Filme que aparece no final deste tutorial.)

if(IsPost){
    Validation.RequireField("title", "You must enter a title");
    Validation.RequireField("genre", "Genre is required");
    Validation.RequireField("year", "You haven't entered a year");
    Validation.RequireField("movieid", "No movie ID was submitted!");

    title = Request.Form["title"];
    genre = Request.Form["genre"];
    year = Request.Form["year"];
    movieId = Request.Form["movieId"];

    if(Validation.IsValid()){
        var db = Database.Open("WebPagesMovies");
        var updateCommand = "UPDATE Movies SET Title=@0, Genre=@1, Year=@2 WHERE Id=@3";
        db.Execute(updateCommand, title, genre, year, movieId);
        Response.Redirect("~/Movies");
   }
}

Mais uma vez, esta marcação e código são semelhantes ao código do AddMovie. O código está num if(IsPost) bloco, porque este código só é executado quando o utilizador clica no botão Enviar Alterações — ou seja, quando (e só quando) o formulário foi publicado. Neste caso, não está a usar um teste como if(IsPost && Validation.IsValid())— ou seja, não está a combinar ambos os testes ao usar AND. Nesta página, primeiro determina se há uma submissão de formulário (if(IsPost)), e só depois regista os campos para validação. Depois podes testar os resultados da validação (if(Validation.IsValid()). O fluxo é ligeiramente diferente da página AddMovie.cshtml , mas o efeito é o mesmo.

Obtém-se os valores das caixas de texto usando Request.Form["title"] e um código semelhante para os elementos <input>. Repare que desta vez, o código retira o ID do filme do campo oculto (<input type="hidden">). Quando a página foi executada pela primeira vez, o código obteve o ID da string de consulta. Obtém-se o valor do campo oculto para garantir que está a obter o ID do filme que foi originalmente exibido, caso a cadeia de consulta tenha sido alterada desde então.

A diferença realmente importante entre o código do AddMovie e este código é que, neste código, usas a instrução SQL Update em vez da Insert Into instrução. O exemplo seguinte mostra a sintaxe da instrução SQL Update :

UPDATE table SET col1="value", col2="value", col3="value" ... WHERE ID = value

Podes especificar quaisquer colunas em qualquer ordem, e não tens necessariamente de atualizar todas as colunas durante uma Update operação. (Não pode atualizar o ID em si, porque isso na prática guardaria o registo como novo, e isso não é permitido para uma Update operação.)

Observação

Importante A Where cláusula com o ID é muito importante, porque é assim que a base de dados sabe qual registo da base de dados quer atualizar. Se deixasse a Where cláusula de fora, a base de dados atualizaria todos os registos da base de dados. Na maioria dos casos, isso seria um desastre.

No código, os valores a atualizar são passados para a instrução SQL usando marcadores de posição. Para repetir o que já dissemos antes: por razões de segurança, usa apenas marcadores para passar valores a uma instrução SQL.

Depois de o código usar db.Execute para executar a instrução Update, redireciona para a página de listagem, onde pode ver as alterações.

Sugestão

Diferentes Instruções SQL, Métodos Diferentes

Talvez tenhas reparado que usas métodos ligeiramente diferentes para executar diferentes instruções SQL. Para executar uma Select consulta que potencialmente devolve múltiplos registos, utiliza-se o Query método. Para executar uma Select consulta que saiba que vai devolver apenas um item da base de dados, usa o QuerySingle método. Para executar comandos que fazem alterações mas que não devolvem itens da base de dados, usas o Execute método.

Tem de ter métodos diferentes porque cada um deles retorna resultados diferentes, como já viu na diferença entre Query e QuerySingle. (O Execute método também devolve um valor — nomeadamente, o número de linhas da base de dados afetadas pelo comando — mas até agora tem ignorado isso.)

Claro que o Query método pode devolver apenas uma linha de base de dados. No entanto, ASP.NET trata sempre os resultados do Query método como uma coleção. Mesmo que o método devolva apenas uma linha, tens de extrair essa única linha da coleção. Portanto, em situações em que sabe que só vai receber uma linha de volta, é um pouco mais conveniente usar QuerySingle.

Existem alguns outros métodos que realizam tipos específicos de operações de base de dados. Pode encontrar uma lista de métodos de base de dados na ASP.NET Web Pages API Quick Reference.

Tornando a validação do ID mais robusta

Na primeira vez que a página é executada, obtém-se o ID do filme a partir da string de consulta para poder buscar esse filme na base de dados. Certificaste-te de que havia realmente um valor a procurar, o que fizeste usando este código:

if(!IsPost){
    if(!Request.QueryString["ID"].IsEmpty()){
        // Etc.
    }
}

Usou este código para garantir que, se um utilizador acedisse à página EditMovies sem primeiro selecionar um filme na página Movies , a página mostraria uma mensagem de erro fácil de usar. (Caso contrário, os utilizadores veriam um erro que provavelmente só os confundiria.)

No entanto, esta validação não é muito robusta. A página também pode ser invocada com estes erros:

  • O cartão de identificação não é um número. Por exemplo, a página pode ser invocada com uma URL como http://localhost:nnnnn/EditMovie?id=abc.
  • O ID é um número, mas refere-se a um filme que não existe (por exemplo, http://localhost:nnnnn/EditMovie?id=100934).

Se estiver curioso para ver os erros que resultam destes endereços URL, abra a página Filmes. Selecione um filme para editar e depois altere o URL da página EditMovie para um URL que contenha um ID alfabético ou o ID de um filme inexistente.

Então, o que deves fazer? A primeira correção é garantir que não só seja passado um ID para a página, mas que esse ID seja um inteiro. Muda o código do !IsPost teste para se assemelhar a este exemplo:

if(!IsPost){
    if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].IsInt()) {
       // Etc.

Adicionou uma segunda condição ao IsEmpty teste, ligada a && (AND lógico):

Request.QueryString["ID"].IsInt()

Talvez se lembre do tutorial de Introdução à Programação de Páginas Web ASP.NET, em que funções como AsBool convertem AsInt uma cadeia de caracteres em outro tipo de dado. O IsInt método (e outros, como IsBool e IsDateTime) são semelhantes. No entanto, testam apenas se consegues converter a cadeia, sem realmente realizar a conversão. Portanto, aqui estás basicamente a dizer: Se o valor da string de consulta pode ser convertido num inteiro....

O outro problema potencial é procurar um filme que não existe. O código para obter um filme parece este código:

var row = db.QuerySingle(dbCommand, movieId);

Se passar um movieId valor ao QuerySingle método que não corresponde a um filme real, nada é devolvido e as instruções que se seguem (por exemplo, title=row.Title) resultam em erros.

Mais uma vez, há uma solução fácil. Se o db.QuerySingle método não devolver resultados, a row variável será nula. Assim, podes verificar se a row variável é nula antes de tentares obter valores dela. O código seguinte adiciona um if bloco em torno das instruções que extraem os valores do row objeto:

if(row != null) {
    title = row.Title;
    genre = row.Genre;
    year = row.Year;
}
else{
    Validation.AddFormError("No movie was found for that ID.");
}

Com estes dois testes adicionais de validação, a página torna-se mais à prova de balas. O código completo do !IsPost ramo agora é este exemplo:

if(!IsPost){
    if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].IsInt()) {
        movieId = Request.QueryString["ID"];
        var db = Database.Open("WebPagesMovies");
        var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
        var row = db.QuerySingle(dbCommand, movieId);

        if(row != null) {
            title = row.Title;
            genre = row.Genre;
            year = row.Year;
        }
        else {
            Validation.AddFormError("No movie was found for that ID.");
        }
    }
    else {
        Validation.AddFormError("No movie was selected.");
    }
}

Referimos mais uma vez que esta tarefa é um bom uso para um bloco else. Se os testes não passarem, os blocos else definem mensagens de erro.

Um detalhe final e útil é adicionar um link de volta à página de Filmes . No fluxo normal dos acontecimentos, os utilizadores começam na página de Filmes e clicam num link Editar . Isso leva-os à página EditMovie , onde podem editar o filme e clicar no botão. Depois de o código processar a alteração, redireciona de volta para a página de Filmes .

No entanto:

  • O utilizador pode decidir não alterar nada.
  • O utilizador pode ter acedido a esta página sem antes clicar num link de Editar na página de Filmes .

De qualquer forma, você quer facilitar para eles o regresso à lista principal. É uma solução fácil — adicione a seguinte marcação logo após a etiqueta de fecho </form> na marcação:

<p><a href="~/Movies">Return to movie listing</a></p>

A marcação utiliza a mesma sintaxe para o elemento <a> que você já viu noutras partes. O URL inclui ~ para significar "raiz do site."

Testar o Processo de Atualização de Filmes

Agora podes testar. Abre a página de Filmes e clica em Editar ao lado de um filme. Quando aparecer a página EditMovie , faça alterações ao filme e clique em Enviar Alterações. Quando aparecer a lista de filmes, certifique-se de que as suas alterações são mostradas.

Para garantir que a validação está a funcionar, clique em Editar para outro filme. Quando chegares à página EditMovie , limpa o campo Género (ou Ano , ou ambos) e tenta submeter as tuas alterações. Verá um erro, como seria de esperar:

Editar página de Filme mostrando erros de validação

Clique no link Voltar à lista de filmes para abandonar as alterações e voltar à página de Filmes .

A seguir

No próximo tutorial, vais ver como apagar um registo de filme.

@{
    var db = Database.Open("WebPagesMovies") ;
    var selectCommand = "SELECT * FROM Movies";
    var searchTerm = "";

    if(!Request.QueryString["searchGenre"].IsEmpty() ) {
        selectCommand = "SELECT * FROM Movies WHERE Genre = @0";
        searchTerm = Request.QueryString["searchGenre"];
    }

    if(!Request.QueryString["searchTitle"].IsEmpty() ) {
        selectCommand = "SELECT * FROM Movies WHERE Title LIKE @0";
        searchTerm = "%" + Request.QueryString["searchTitle"] + "%";
    }

    var selectedData = db.Query(selectCommand, searchTerm);
    var grid = new WebGrid(source: selectedData, defaultSort: "Genre", rowsPerPage:3);
}

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>Movies</title>
        <style type="text/css">
          .grid { margin: 4px; border-collapse: collapse; width: 600px; }
          .grid th, .grid td { border: 1px solid #C0C0C0; padding: 5px; }
          .head { background-color: #E8E8E8; font-weight: bold; color: #FFF; }
          .alt { background-color: #E8E8E8; color: #000; }
        </style>
    </head>
    <body>
        <h1>Movies</h1>
          <form method="get">
              <div>
                <label for="searchGenre">Genre to look for:</label>
                <input type="text" name="searchGenre" value="@Request.QueryString["searchGenre"]" />
                <input type="Submit" value="Search Genre" /><br/>
                (Leave blank to list all movies.)<br/>
                </div>

              <div>
                  <label for="SearchTitle">Movie title contains the following:</label>
                  <input type="text" name="searchTitle" value="@Request.QueryString["searchTitle"]" />
                  <input type="Submit" value="Search Title" /><br/>
                </div>
            </form>

        <div>
             @grid.GetHtml(
                tableStyle: "grid",
                headerStyle: "head",
                alternatingRowStyle: "alt",
                columns: grid.Columns(
                    grid.Column(format: @<a href="~/EditMovie?id=@item.ID">Edit</a>),
                    grid.Column("Title"),
                    grid.Column("Genre"),
                    grid.Column("Year")
                )
            )
        </div>
    <p>
        <a href="~/AddMovie">Add a movie</a>
    </p>
    </body>
</html>

Lista completa de páginas para a página de Editar Filme

@{
    var title = "";
    var genre = "";
    var year = "";
    var movieId = "";

    if(!IsPost){
        if(!Request.QueryString["ID"].IsEmpty() && Request.QueryString["ID"].IsInt()) {
            movieId = Request.QueryString["ID"];
            var db = Database.Open("WebPagesMovies");
            var dbCommand = "SELECT * FROM Movies WHERE ID = @0";
            var row = db.QuerySingle(dbCommand, movieId);

            if(row != null) {
                title = row.Title;
                genre = row.Genre;
                year = row.Year;
            }
            else{
                Validation.AddFormError("No movie was selected.");
            }
        }
        else{
            Validation.AddFormError("No movie was selected.");
        }
    }

    if(IsPost){
        Validation.RequireField("title", "You must enter a title");
        Validation.RequireField("genre", "Genre is required");
        Validation.RequireField("year", "You haven't entered a year");
        Validation.RequireField("movieid", "No movie ID was submitted!");

        title = Request.Form["title"];
        genre = Request.Form["genre"];
        year = Request.Form["year"];
        movieId = Request.Form["movieId"];

        if(Validation.IsValid()){
            var db = Database.Open("WebPagesMovies");
            var updateCommand = "UPDATE Movies SET Title=@0, Genre=@1, Year=@2 WHERE Id=@3";
            db.Execute(updateCommand, title, genre, year, movieId);
            Response.Redirect("~/Movies");
        }
    }
}

<!DOCTYPE html>
<html>
  <head>
   <meta charset="utf-8" />
   <title>Edit a Movie</title>
    <style>
      .validation-summary-errors{
        border:2px dashed red;
        color:red;
        font-weight:bold;
        margin:12px;
      }
    </style>
  </head>
  <body>
    <h1>Edit a Movie</h1>
      @Html.ValidationSummary()
      <form method="post">
      <fieldset>
        <legend>Movie Information</legend>

        <p><label for="title">Title:</label>
           <input type="text" name="title" value="@title" /></p>

        <p><label for="genre">Genre:</label>
           <input type="text" name="genre" value="@genre" /></p>

        <p><label for="year">Year:</label>
           <input type="text" name="year" value="@year" /></p>

        <input type="hidden" name="movieid" value="@movieId" />

        <p><input type="submit" name="buttonSubmit" value="Submit Changes" /></p>
      </fieldset>
    </form>
    <p><a href="~/Movies">Return to movie listing</a></p>
  </body>
</html>

Recursos adicionais