Presentación de ASP.NET Web Pages: actualización de datos de base de datos

Por Tom FitzMacken

En este tutorial se muestra cómo actualizar (cambiar) una entrada de base de datos existente al usar ASP.NET Web Pages (Razor). Se supone que ha completado la serie con la introducción de datos mediante formularios mediante ASP.NET Web Pages.

Temas que se abordarán:

  • Selección de un registro individual en el asistente WebGrid.
  • Lectura de un único registro de una base de datos.
  • Precarga de un formulario con valores del registro de base de datos.
  • Actualización de un registro existente en una base de datos.
  • Almacenamiento de información en la página sin mostrarla.
  • Uso de un campo oculto para almacenar información.

Características y tecnologías descritas:

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

Lo que creará

En el tutorial anterior, ha aprendido a agregar un registro a una base de datos. Aquí aprenderá a mostrar un registro para su edición. En la página Películas, actualizará el asistente WebGrid para que muestre un vínculo Editar junto a cada película:

WebGrid display including an 'Edit' link for each movie

Al hacer clic en el vínculo Editar, se le lleva a otra página, donde la información de la película ya está en un formulario:

Edit Movie page showing movie to be edited

Puede cambiar cualquiera de los valores. Al enviar los cambios, el código de la página actualiza la base de datos y le lleva de vuelta a la lista de películas.

Esta parte del proceso funciona casi exactamente igual que la página AddMovie.cshtml que creó en el tutorial anterior, por lo que gran parte de este tutorial será familiar.

Hay varias maneras de implementar una manera de editar una película individual. Se ha elegido el enfoque que se muestra porque es fácil de implementar y fácil de entender.

Para empezar, actualizará la página Películas para que cada lista de películas también contenga un vínculo Editar.

Abra el archivo Movies.cshtml.

En el cuerpo de la página, cambie el marcado WebGrid agregando una columna. Este es el marcado modificado:

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

La nueva columna es esta:

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

El punto de esta columna es mostrar un vínculo (elemento <a>) cuyo texto dice "Editar". Lo que buscamos es crear un vínculo similar al siguiente cuando se ejecuta la página, con el valor id diferente para cada película:

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

Este vínculo invocará una página denominada EditMovie y pasará la cadena de consulta ?id=7 a esa página.

La sintaxis de la nueva columna podría parecer un poco compleja, pero es solo porque reúne varios elementos. Cada elemento individual es sencillo. Si solo se centra en el elemento <a>, verá este marcado:

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

Algunos antecedentes sobre cómo funciona la cuadrícula: la cuadrícula muestra filas, una para cada registro de base de datos y muestra columnas para cada campo del registro de base de datos. Mientras se construye cada fila de cuadrícula, el objeto item contiene el registro de base de datos (elemento) de esa fila. Esta disposición le ofrece una manera de obtener los datos de esa fila. Esto es lo que ve aquí: la expresión item.ID obtiene el valor de identificador del elemento de base de datos actual. Puede obtener cualquiera de los valores de base de datos (título, género o año) de la misma manera mediante item.Title, item.Genre o item.Year.

La expresión "~/EditMovie?id=@item.ID combina la parte codificada de forma rígida de la dirección URL de destino (~/EditMovie?id=) con este identificador derivado dinámicamente. (Ha visto el operador ~ en el tutorial anterior; es un operador de ASP.NET que representa la raíz del sitio web actual).

El resultado es que esta parte del marcado de la columna simplemente genera algo parecido al marcado siguiente en tiempo de ejecución:

href="/EditMovie?id=2"

Naturalmente, el valor real de id será diferente para cada fila.

Creación de una presentación personalizada para una columna de cuadrícula

Ahora volvamos a la columna de cuadrícula. Las tres columnas que originalmente tenía en la cuadrícula solo mostraban valores de datos (título, género y año). Especificó esta presentación pasando el nombre de la columna de base de datos , por ejemplo, grid.Column("Title").

Esta nueva columna del vínculo Edit es diferente. En lugar de especificar un nombre de columna, se pasa un parámetro format. Este parámetro le permite definir el marcado que el asistente WebGrid representará junto con el valor item para mostrar los datos de columna como negrita o verde o en el formato que desee. Por ejemplo, si desea que el título aparezca en negrita, podría crear una columna como la de este ejemplo:

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

(Los distintos caracteres @ que ve en la propiedad format marcan la transición entre marcado y un valor de código).

Una vez que conozca la propiedad format, es más fácil entender cómo se reúne la nueva columna de vínculo Edit:

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

La columna consta solo del marcado que representa el vínculo, además de cierta información (el identificador) que se extrae del registro de base de datos de la fila.

Sugerencia

Parámetros con nombre y parámetros posicionales para un método

Muchas veces cuando ha llamado a un método y ha pasado parámetros a él, simplemente ha enumerado los valores de parámetro separados por comas. Estos son algunos ejemplos:

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

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

No mencionamos el problema cuando vio este código por primera vez, pero, en cada caso, se pasan parámetros a los métodos en un orden específico, es decir, el orden en el que se definen los parámetros en ese método. Para db.Execute y Validation.RequireFields, si mezcla el orden de los valores que pasa, recibirá un mensaje de error cuando se ejecute la página o, al menos, algunos resultados extraños. Evidentemente, tiene que saber el orden en el que debe pasar los parámetros. (En WebMatrix, IntelliSense puede ayudarle a averiguar el nombre, el tipo y el orden de los parámetros).

Como alternativa a pasar valores en orden, puede usar parámetros con nombre. (Pasar parámetros en orden se conoce como usar parámetros posicionales). Para los parámetros con nombre, se incluye explícitamente el nombre del parámetro al pasar su valor. Ya ha usado parámetros con nombre varias veces en estos tutoriales. Por ejemplo:

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

y

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

Los parámetros con nombre son útiles para un par de situaciones, especialmente cuando un método toma muchos parámetros. Uno es cuando desea pasar solo uno o dos parámetros, pero los valores que desea pasar no están entre las primeras posiciones de la lista de parámetros. Otra situación es cuando quiere que el código sea más legible pasando los parámetros en el orden que tenga más sentido.

Obviamente, para usar parámetros con nombre, debe conocer los nombres de los parámetros. WebMatrix IntelliSense puede mostrar los nombres, pero de momento no puede rellenarlos automáticamente.

Creación de la página Edit

Ahora puede crear la página EditMovie. Cuando los usuarios hagan clic en el vínculo Edit, terminarán en esta página.

Cree una página denominada EditMovie.cshtml y reemplace lo que hay en el archivo por el marcado siguiente:

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

Este marcado y código son similares a los que tiene en la página AddMovie. Hay una pequeña diferencia en el texto del botón Submit. Al igual que con la página AddMovie, hay una llamada Html.ValidationSummary que mostrará errores de validación si hay alguno. Esta vez vamos a omitir las llamadas a Validation.Message, ya que los errores se mostrarán en el resumen de validación. Como se indicó en el tutorial anterior, puede usar el resumen de validación y los mensajes de error individuales en varias combinaciones.

Observe de nuevo que el atributo method del elemento <form> está establecido en post. Al igual que con la página AddMovie.cshtml, esta página realiza cambios en la base de datos. Por lo tanto, este formulario debe realizar una operación POST. (Para obtener más información sobre la diferencia entre las operaciones GET y POST, consulte la barra lateral Seguridad de los verbos GET, POST y HTTP en el tutorial sobre formularios HTML).

Como ha visto en un tutorial anterior, los atributos value de los cuadros de texto se establecen con código de Razor para cargarlos previamente. Sin embargo, esta vez usa variables como title y genre para esa tarea en lugar de Request.Form["title"]:

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

Como antes, este marcado cargará previamente los valores del cuadro de texto con los valores de la película. Verá en un momento por qué resulta útil usar variables esta vez en lugar de usar el objeto Request.

También hay un elemento <input type="hidden"> en esta página. Este elemento almacena el identificador de la película sin que sea visible en la página. El identificador se pasa inicialmente a la página mediante un valor de cadena de consulta (?id=7 o similar en la dirección URL). Al colocar el valor del identificador en un campo oculto, puede asegurarse de que está disponible cuando se envía el formulario, incluso si ya no tiene acceso a la dirección URL original con la que se invocó la página.

A diferencia de la página AddMovie, el código de la página EditMovie tiene dos funciones distintas. La primera función es que cuando la página se muestra por primera vez (y solo entonces), el código obtiene el identificador de la película de la cadena de consulta. A continuación, el código usa el identificador para leer la película correspondiente fuera de la base de datos y mostrarla (precargar) en los cuadros de texto.

La segunda función es que cuando el usuario hace clic en el botón Submit changes, el código tiene que leer los valores de los cuadros de texto y validarlos. El código también tiene que actualizar el elemento de base de datos con los nuevos valores. Esta técnica es similar a agregar un registro, como ha visto en AddMovie.

Adición de código para leer una sola película

Para realizar la primera función, agregue este código a la parte superior de la 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.");
        }
    }
}

La mayoría de este código está dentro de un bloque que inicia if(!IsPost). El operador ! significa "no", por lo que la expresión significa si esta solicitud no es un envío posterior, que es una manera indirecta de decir si esta solicitud es la primera vez que esta página se ha ejecutado. Como se indicó anteriormente, este código solo debe ejecutarse la primera vez que se ejecuta la página. Si no incluye el código en if(!IsPost), se ejecutaría cada vez que se invoca la página, ya sea la primera vez o en respuesta a un clic de botón.

Observe que el código incluye un bloque else esta vez. Como hemos dicho cuando se introdujeron los bloques if, a veces quiere ejecutar código alternativo si la condición que está probando no es cierta. Ese es el caso aquí. Si la condición pasa (es decir, si el identificador pasado a la página es correcto), se lee una fila de la base de datos. Sin embargo, si la condición no se pasa, el bloque else se ejecuta y el código establece un mensaje de error.

Validación de un valor pasado a la página

El código usa Request.QueryString["id"] para obtener el identificador que se pasa a la página. El código se asegura de que se pasó realmente un valor para el identificador. Si no se pasó ningún valor, el código establece un error de validación.

Este código muestra una manera diferente de validar la información. En el tutorial anterior, ha trabajado con el asistente Validation. Ha registrado campos para validar y ASP.NET realizó automáticamente la validación y mostró errores mediante Html.ValidationMessage y Html.ValidationSummary. Sin embargo, en este caso no está validando realmente la entrada de usuario. En su lugar, va a validar un valor que se pasó a la página desde otro lugar. El asistente Validationno lo hace por usted.

Por lo tanto, tiene que comprobar el valor, probando con if(!Request.QueryString["ID"].IsEmpty()). Si hay un problema, puede mostrar el error mediante Html.ValidationSummary, como hizo con el asistente Validation. Para ello, llame a Validation.AddFormError y páselo un mensaje para mostrarlo. Validation.AddFormError es un método integrado que permite definir mensajes personalizados que se vinculan con el sistema de validación con el que ya está familiarizado. (Más adelante en este tutorial hablaremos sobre cómo hacer que este proceso de validación sea un poco más sólido).

Después de asegurarse de que hay un identificador para la película, el código lee la base de datos, buscando solo un único elemento de base de datos. (Probablemente haya observado el patrón general para las operaciones de base de datos: abrir la base de datos, definir una instrucción SQL y ejecutar la instrucción). Esta vez, la instrucción SQL Select incluye WHERE ID = @0. Dado que el identificador es único, solo se puede devolver un registro.

La consulta se realiza mediante db.QuerySingle (no db.Query, como usó para la lista de películas) y el código coloca el resultado en la variable row. El nombre row es arbitrario; puede asignar un nombre a las variables que quiera. Las variables inicializadas en la parte superior se rellenan con los detalles de la película para que estos valores se puedan mostrar en los cuadros de texto.

Prueba de la página Edit (hasta ahora)

Si quiere probar la página, ejecute la página Movies ahora y haga clic en un vínculo Edit junto a cualquier película. Verá la página EditMovie con los detalles rellenados para la película que seleccionó:

Screenshot shows the Edit Movie page showing movie to be edited.

Observe que la dirección URL de la página incluye algo parecido a ?id=10 (o algún otro número). Hasta ahora, ha probado que los vínculos Edit de la página Movie funcionan, que la página está leyendo el identificador de la cadena de consulta y que la consulta de base de datos para obtener un único registro de película funciona.

Puede cambiar la información de la película, pero no sucede nada al hacer clic en Submit changes.

Adición de código para actualizar la película con los cambios del usuario

En el archivo EditMovie.cshtml, para implementar la segunda función (guardar los cambios), agregue el código siguiente justo dentro de la llave de cierre del bloque @. (Si no sabe exactamente dónde colocar el código, puede ver la lista de código completa de la página Edit Movie que aparece al final de este 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");
   }
}

De nuevo, este marcado y código es similar al código de AddMovie. El código está en un bloque if(IsPost), ya que este código solo se ejecuta cuando el usuario hace clic en el botón Submit Changes, es decir, cuando (y solo cuando) se ha publicado el formulario. En este caso, no está usando una prueba como if(IsPost && Validation.IsValid()), es decir, no está combinando ambas pruebas mediante AND. En esta página, primero determinará si hay un envío de formulario (if(IsPost)) y solo entonces registrará los campos para la validación. A continuación, puede probar los resultados de la validación (if(Validation.IsValid()). El flujo es ligeramente diferente que en la página AddMovie.cshtml, pero el efecto es el mismo.

Obtiene los valores de los cuadros de texto mediante Request.Form["title"] y código similar para los demás elementos <input>. Observe que esta vez el código obtiene el identificador de película del campo oculto (<input type="hidden">). Cuando la página se ejecutó por primera vez, el código obtuvo el identificador de la cadena de consulta. Obtiene el valor del campo oculto para asegurarse de que recibe el identificador de la película que se mostró originalmente, en caso de que la cadena de consulta se modificara de alguna manera desde entonces.

La diferencia realmente importante entre el código AddMovie y este código es que en este código se usa la instrucción SQL Update en lugar de la instrucción Insert Into. En el ejemplo siguiente se muestra la sintaxis de la instrucción SQL Update.

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

Puede especificar cualquier columna en cualquier orden y no tiene que actualizar necesariamente todas las columnas durante una operación Update. (No se puede actualizar el mismo identificador, ya que eso guardaría el registro como un nuevo registro y eso no se permite para una operación Update).

Nota:

Importante La cláusula Where con el identificador es muy importante, ya que así es como la base de datos sabe qué registro de base de datos desea actualizar. Si omitiera la cláusula Where, la base de datos actualizaría todos los registros de la base de datos. En la mayoría de los casos, eso sería un desastre.

En el código, los valores que se van a actualizar se pasan a la instrucción SQL mediante marcadores de posición. Para repetir lo que hemos dicho antes: por motivos de seguridad, use solo marcadores de posición para pasar valores a una instrucción SQL.

Cuando el código usa db.Execute para ejecutar la instrucción Update, se redirige de nuevo a la página de lista, donde puede ver los cambios.

Sugerencia

Diferentes instrucciones SQL, métodos distintos

Es posible que haya observado que usa métodos ligeramente diferentes para ejecutar instrucciones SQL distintas. Para ejecutar una consulta Select que pueda devolver varios registros, use el método Query. Para ejecutar una consulta Select que sepa que devolverá solo un elemento de base de datos, use el método QuerySingle. Para ejecutar comandos que realicen cambios, pero que no devuelvan elementos de base de datos, use el método Execute.

Debe tener métodos diferentes porque cada uno de ellos devuelve resultados diferentes, como ya ha visto en la diferencia entre Query y QuerySingle. (El método Execute devuelve realmente un valor, es decir, el número de filas de base de datos afectadas por el comando, pero hasta ahora se ha ignorado).

Por supuesto, el método Query podría devolver solo una fila de base de datos. Sin embargo, ASP.NET siempre trata los resultados del método Query como una colección. Incluso si el método devuelve solo una fila, debe extraer esa sola fila de la colección. Por lo tanto, en situaciones en las que sabe que solo recuperará una fila, es un poco más conveniente usar QuerySingle.

Hay algunos otros métodos que realizan tipos específicos de operaciones de base de datos. Puede encontrar una lista de métodos de base de datos en la referencia rápida de la API de ASP.NET Web Pages.

Validación del identificador más sólida

La primera vez que se ejecuta la página, obtendrá el identificador de película de la cadena de consulta para que pueda recibir esa película de la base de datos. Se ha asegurado de que realmente hay un valor para buscar, que ha hecho mediante este código:

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

Usó este código para asegurarse de que si un usuario llega a la página EditMovies sin seleccionar primero una película en la página Movies, la página mostraría un mensaje de error descriptivo. (De lo contrario, los usuarios verían un error que probablemente simplemente los confundiera).

Sin embargo, esta validación no es muy sólida. La página también se puede invocar con estos errores:

  • El identificador no es un número. Por ejemplo, la página podría invocarse con una dirección URL como http://localhost:nnnnn/EditMovie?id=abc.
  • El identificador es un número, pero hace referencia a una película que no existe (por ejemplo, http://localhost:nnnnn/EditMovie?id=100934).

Si tiene curiosidad por ver los errores resultantes de estas direcciones URL, ejecute la página Movies. Seleccione una película para editar y, a continuación, cambie la dirección URL de la página EditMovie a una dirección URL que contenga un identificador alfabético o el identificador de una película inexistente.

¿Qué debe usar? La primera corrección consiste en asegurarse de que no solo se pase un identificador a la página, sino que el identificador sea un entero. Cambie el código de la prueba !IsPost para que tenga un aspecto similar al de este ejemplo:

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

Ha agregado una segunda condición a la prueba IsEmpty, vinculada a && (AND lógico):

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

Es posible que recuerde del tutorial Introducción a la programación de ASP.NET Web Pages que métodos como AsBool y AsInt convierten una cadena de caracteres en algún otro tipo de datos. El método IsInt (y otros, como IsBool y IsDateTime) son similares. Sin embargo, solo prueban si se puede convertir la cadena, sin realizar realmente la conversión. Por lo tanto, aquí se dice básicamente que si el valor de la cadena de consulta se puede convertir en un entero....

El otro posible problema es buscar una película que no existe. El código para obtener una película tiene el siguiente aspecto:

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

Si pasa un valor movieId al método QuerySingle que no corresponde a una película real, no se devuelve nada y las instrucciones siguientes (por ejemplo, title=row.Title) producen errores.

De nuevo hay una solución fácil. Si el método db.QuerySingle no devuelve ningún resultado, la variable row será un valor null. Por lo tanto, puede comprobar si la variable row es null antes de intentar obtener valores de ella. El código siguiente agrega un bloque if alrededor de las instrucciones que obtienen los valores del objeto row:

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

Con estas dos pruebas de validación adicionales, la página se vuelve más sólida. El código completo de la rama !IsPost ahora tiene el siguiente aspecto:

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.");
    }
}

Observaremos una vez más que esta tarea es un buen uso para un bloque else. Si las pruebas no se superan, los bloques else establecen mensajes de error.

Un detalle final y útil es agregar un vínculo a la página Movies. En el flujo normal de eventos, los usuarios comenzarán en la página Movies y harán clic en un vínculo Edit. Eso los lleva a la página EditMovie, donde pueden editar la película y hacer clic en el botón. Cuando el código procesa el cambio, vuelve a la página Movies.

Pero:

  • Es posible que el usuario decida no cambiar nada.
  • Es posible que el usuario haya llegado a esta página sin hacer clic primero en un vínculo Edit en la página Movies.

En cualquier caso, quiere facilitar que vuelvan a la lista principal. Es una corrección sencilla: agregue el siguiente marcado justo después de la etiqueta de cierre </form> en el marcado:

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

Este marcado usa la misma sintaxis para un elemento <a> que ha visto en otro lugar. La dirección URL incluye ~ que significa "raíz del sitio web".

Prueba del proceso de actualización de películas

Ahora puede probarlo. Ejecute la página Movies y haga clic en Edit junto a una película. Cuando aparezca la página EditMovie, realice cambios en la película y haga clic en Submit Changes. Cuando aparezca la lista de películas, asegúrese de que se muestran los cambios.

Para asegurarse de que la validación funciona, haga clic en Edit para otra película. Cuando llegue a la página EditMovie, desactive el campo Genre (o campo Year, o ambos) e intente enviar los cambios. Verá un error, como cabría esperar:

Edit Movie page showing validation errors

Haga clic en el vínculo Return to movie listing para abandonar los cambios y volver a la página Movies.

Próximamente

En el siguiente tutorial, verá cómo eliminar un registro de película.

@{
    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 la página de edición de películas

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