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

por Tom FitzMacken

En este tutorial se muestra cómo eliminar una entrada de base de datos individual. Se supone que ha completado la serie con Actualización de datos de bases de datos en ASP.NET Web Pages.

Temas que se abordarán:

  • Selección de un registro individual de una lista de registros.
  • Eliminación de un único registro de una base de datos.
  • Cómo comprobar que se hizo clic en un botón específico en un formulario.

Características y tecnologías descritas:

  • El asistente WebGrid.
  • El comando Delete SQL.
  • Método Database.Execute para ejecutar un comando Delete SQL.

Lo que creará

En el tutorial anterior, ha aprendido a actualizar un registro de base de datos existente. Este tutorial es similar, excepto que en lugar de actualizar el registro, lo eliminará. Los procesos son muy parecidos, salvo que la eliminación es más sencilla, por lo que este tutorial será breve.

En la página Películas, actualizará el asistente WebGrid para que muestre un vínculo Eliminar junto a cada película que acompañe al vínculo Editar que agregó anteriormente.

Movies page showing a Delete link for each movie

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

Delete Movie page with a movie displayed

A continuación, puede hacer clic en el botón para eliminar el registro de forma permanente.

Para empezar, agregará un vínculo Eliminar al asistente WebGrid. Este vínculo es similar al vínculo Editar que agregó en un tutorial anterior.

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"),
grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
    )
)

La nueva columna es esta:

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

Con la forma en que se ha configura la cuadrícula, la columna Editar se encuentra completamente a la izquierda en la cuadrícula y la columna Eliminar completamente a la derecha. (Ahora, hay una coma después de la columna Year, por si no lo ha notado). No hay nada especial sobre dónde van estas columnas de vínculo, y podría ponerlas fácilmente una al lado de la otra. En este caso, son independientes para que sean más difíciles de mezclar.

Movies page with Edit and Details links marked to show that they're not next to each other

La nueva columna muestra un vínculo (elemento <a>) cuyo texto dice "Eliminar". El destino del vínculo (su atributo href) es código que, en última instancia, se resuelve en algo parecido a esta dirección URL, con el valor id diferente para cada película:

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

Este vínculo invocará una página denominada DeleteMovie y le pasará el identificador de la película que ha seleccionado.

Este tutorial no profundizará en cómo se crea este vínculo, ya que es casi idéntico al vínculo Editar del tutorial anterior (Actualización de datos de base de datos en ASP.NET Web Pages).

Creación de la página Eliminar

Ahora puede crear la página que será el destino del vínculo Eliminar en la cuadrícula.

Nota:

Importante: La técnica de seleccionar primero un registro para eliminar y, a continuación, usar una página y un botón independientes para confirmar el proceso es extremadamente importante para la seguridad. Como ha leído en los tutoriales anteriores, realizar cualquier tipo de cambio en su sitio web siempre debe realizarse con un formulario, es decir, mediante una operación HTTP POST. Si fuera posible cambiar de sitio con solo hacer clic en un vínculo (es decir, mediante una operación GET), los usuarios podrían hacer solicitudes sencillas a su sitio y eliminar sus datos. Incluso un rastreador del motor de búsqueda que indexa el sitio podría eliminar accidentalmente los datos simplemente con seguir los vínculos.

Cuando la aplicación permite modificar un registro, tiene que presentar el registro al usuario para que lo edite de todos modos. Pero es posible que tenga la tentación de omitir este paso para eliminar un registro. Sin embargo, no lo haga. (También resulta útil que los usuarios vean el registro y confirmen que están eliminando el registro que tenían previsto).

En un conjunto de tutoriales posterior, verá cómo agregar la funcionalidad de inicio de sesión para que un usuario tenga que iniciar sesión antes de eliminar un registro.

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

<html>
<head>
  <title>Delete a Movie</title>
</head>
<body>
      <h1>Delete a Movie</h1>
        @Html.ValidationSummary()
      <p><a href="~/Movies">Return to movie listing</a></p>

      <form method="post">
        <fieldset>
        <legend>Movie Information</legend>

        <p><span>Title:</span>
         <span>@title</span></p>

        <p><span>Genre:</span>
         <span>@genre</span></p>

        <p><span>Year:</span>
          <span>@year</span></p>

        <input type="hidden" name="movieid" value="@movieId" />
        <p><input type="submit" name="buttonDelete" value="Delete Movie" /></p>
        </fieldset>
      </form>
    </body>
</html>

Este marcado es como las páginas EditMovie, excepto que en lugar de usar cuadros de texto (<input type="text">), el marcado incluye elementos <span>. No hay nada aquí para editar. Todo lo que tiene que hacer es mostrar los detalles de la película para que los usuarios puedan asegurarse de que están eliminando la película correcta.

El marcado ya contiene un vínculo que permite al usuario volver a la página de descripción de películas.

Como en la página EditMovie, el identificador de la película seleccionada se almacena en un campo oculto. (Se pasa a la página en primer lugar como un valor de cadena de consulta). Hay una llamada a Html.ValidationSummary que mostrará los errores de validación. En este caso, es posible que el error sea que no se haya pasado ningún identificador de película a la página o que el identificador de película no sea válido. Esta situación podría ocurrir si alguien ejecutó esta página sin seleccionar primero una película en la página Películas.

El título del botón es Eliminar película y su atributo name está establecido en buttonDelete. El atributo name se usará en el código para identificar el botón que envió el formulario.

Tendrá que escribir código para 1) leer los detalles de la película cuando la página se muestre por primera vez y 2) eliminar realmente la película cuando el usuario haga clic en el botón.

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

En la parte superior de la página DeleteMovie.cshtml, agregue el siguiente bloque de código:

@{
    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 found for that ID.");
            }
        }
        else{
            Validation.AddFormError("No movie was found for that ID.");
        }
    }
}

Este marcado es el mismo que el código correspondiente de la página EditMovie. Obtiene el identificador de película fuera de la cadena de consulta y usa el identificador para leer un registro de la base de datos. El código incluye la prueba de validación (IsInt() y row != null) para asegurarse de que el identificador de película que se pasa a la página es válido.

Recuerde que este código solo debe ejecutarse la primera vez que se ejecuta la página. No desea volver a leer el registro de película de la base de datos cuando el usuario hace clic en el botón Eliminar película. Por lo tanto, el código para leer la película está dentro de una prueba que dice if(!IsPost): es decir, si la solicitud no es una operación posterior (envío de formulario).

Incorporación de código para eliminar la película seleccionada

Para eliminar la película cuando el usuario haga clic en el botón, agregue el código siguiente justo dentro de la llave de cierre del bloque @:

if(IsPost && !Request["buttonDelete"].IsEmpty()){
    movieId = Request.Form["movieId"];
    var db = Database.Open("WebPagesMovies");
    var deleteCommand = "DELETE FROM Movies WHERE ID = @0";
    db.Execute(deleteCommand, movieId);
    Response.Redirect("~/Movies");
}

Este código es similar al código para actualizar un registro existente, pero más sencillo. El código básicamente ejecuta una instrucción SQL Delete.

Como en la página EditMovie, el código está en un bloque if(IsPost). Esta vez, la condición if() es un poco más complicada:

if(IsPost && !Request["buttonDelete"].IsEmpty())

Hay dos condiciones aquí. La primera es que se envíe la página, como ha visto antes, if(IsPost).

La segunda condición es !Request["buttonDelete"].IsEmpty(), lo que significa que la solicitud tiene un objeto denominado buttonDelete. Ciertamente, es una forma indirecta de probar qué botón envió el formulario. Si un formulario contiene varios botones de envío, solo aparece el nombre del botón en el que se hizo clic en la solicitud. Por lo tanto, lógicamente, si el nombre de un botón determinado aparece en la solicitud, o tal como se indica en el código, si ese botón no está vacío, ese será el botón que envió el formulario.

El operador && significa "and" (AND lógico). Por lo tanto, toda la condición if es ...

Esta solicitud es una publicación (no una solicitud por primera vez)

AND

ElbuttonDeletebotón era el botón que envió el formulario.

Este formulario (de hecho, esta página) contiene solo un botón, por lo que la prueba adicional para buttonDelete técnicamente no es necesaria. Aún así, está a punto de realizar una operación que eliminará permanentemente los datos. Por lo tanto, querrá estar lo más seguro posible de que solo realiza la operación si el usuario lo ha solicitado explícitamente. Por ejemplo, supongamos que expandió esta página posteriormente y agregó otros botones a ella. Incluso después, el código que elimina la película solo se ejecutará si se hizo clic en el botón buttonDelete.

Como en la página EditMovie, obtendrá el identificador del campo oculto y, a continuación, ejecutará el comando SQL. La sintaxis de la instrucción Delete es:

DELETE FROM table WHERE ID = value

Es fundamental incluir la cláusula WHERE y el identificador. Si deja la cláusula WHERE, se eliminarán todos los registros de la tabla. Como ha visto, pasa el valor de identificador al comando SQL mediante un marcador de posición.

Prueba del proceso de eliminación de películas

Ahora puede probarlo. Ejecute la página Películas y haga clic en Eliminar junto a una película. Cuando aparezca la página DeleteMovie, haga clic en Eliminar película.

Delete Movie page with Delete Movie button highlighted

Al hacer clic en el botón, el código elimina las películas y vuelve a la lista de películas. Allí puede buscar la película eliminada y confirmar que se ha eliminado.

Próximamente

En el siguiente tutorial se muestra cómo dar a todas las páginas del sitio un aspecto y un diseño comunes.

@{
    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"),
                grid.Column(format: @<a href="~/DeleteMovie?id=@item.ID">Delete</a>)
            )
        )
      </div>
      <p>
        <a href="~/AddMovie">Add a movie</a>
      </p>
    </body>
</html>

Lista completa de la página DeleteMovie

@{
    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 found for that ID.");
            }
        }
        else{
            Validation.AddFormError("No movie was found for that ID.");
        }
    }

    if(IsPost && !Request["buttonDelete"].IsEmpty()){
        movieId = Request.Form["movieId"];
        var db = Database.Open("WebPagesMovies");
        var deleteCommand = "DELETE FROM Movies WHERE ID = @0";
        db.Execute(deleteCommand, movieId);
        Response.Redirect("~/Movies");
    }
}
<html>
<head>
  <title>Delete a Movie</title>
</head>
<body>
      <h1>Delete a Movie</h1>
        @Html.ValidationSummary()
      <p><a href="~/Movies">Return to movie listing</a></p>

      <form method="post">
        <fieldset>
        <legend>Movie Information</legend>

        <p><span>Title:</span>
         <span>@title</span></p>

        <p><span>Genre:</span>
         <span>@genre</span></p>

        <p><span>Year:</span>
          <span>@year</span></p>

        <input type="hidden" name="movieid" value="@movieId" />
        <p><input type="submit" name="buttonDelete" value="Delete Movie" /></p>
        </fieldset>
        <p><a href="~/Movies">Return to movie listing</a></p>
      </form>
    </body>
</html>

Recursos adicionales