次の方法で共有


ASP.NET Web ページの概要 - データベース データの削除

作成者: Tom FitzMacken

このチュートリアルでは、個々のデータベース エントリを削除する方法について説明します。 ここでは、「ASP.NET Web ページでのデータベース データの更新」までこのシリーズを完了していることを前提としています。

ここでは、次の内容について学習します。

  • レコードの一覧から個々のレコードを選択する方法。
  • データベースから 1 つのレコードを削除する方法。
  • フォームで特定のボタンがクリックされたことを確認する方法。

対象機能またはテクノロジ:

  • WebGrid ヘルパー。
  • SQL Delete コマンド。
  • SQL の Delete コマンドを実行する Database.Execute メソッド。

作成するアプリケーション:

前のチュートリアルでは、既存のデータベース レコードを更新する方法を学習しました。 このチュートリアルは似ていますが、レコードを更新するのではなく削除するという点が異なります。 それらのプロセスはほぼ同じですが、削除のほうが簡単であるため、このチュートリアルは短くなります。

Movies ページで、以前追加した [編集] リンクに加えて [削除] リンクが各ムービーの横に表示されるように、WebGrid ヘルパーを更新します。

Movies page showing a Delete link for each movie

編集と同様に、[削除] リンクをクリックすると、フォームに既にムービー情報が入力されている、別のページに移動します:

Delete Movie page with a movie displayed

その後、ボタンをクリックするとレコードを完全に削除できます。

最初に、WebGrid ヘルパーに [削除] リンクを追加します。 このリンクは、前のチュートリアルで追加した [編集] リンクに似ています。

Movies.cshtml ファイルを開きます。

ページの本文で列を追加して WebGrid マークアップを変更します。 変更したマークアップを次に示します:

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

新しい列は次のとおりです:

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

このグリッドの構成方法では、[編集] 列がグリッドの左端にあり、[削除] 列が右端にあります。 (お気づきのように、現在は Year 列の後にコンマがあります。)これらのリンク列の配置場所は特に重要ではありません。それらを隣同士に配置することも簡単です。 ここでは、混同しにくいように、それらを別々に配置しています。

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

新しい列には、"削除" というテキストがあるリンク (<a> 要素) が表示されています。 そのリンクのターゲット (その href 属性) は、最終的に次のような URL に解決されるコードであり、ムービーごとにその id 値が異なります:

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

このリンクでは、DeleteMovie という名前のページが呼び出され、選択したムービーの ID がそれに渡されます。

このチュートリアルでは、このリンクの作成方法については詳しく説明しません。これは、前のチュートリアル (ASP.NET Web ページでのデータベース データの更新) の [編集] リンクとほぼ同じであるためです。

削除ページの作成

これで、グリッド内の [削除] リンクのターゲットとなるページを作成できます。

Note

重要 セキュリティのために、削除するレコードを最初に選択してから別のページとボタンを使ってそのプロセスを確認する手法が重要となります。 前のチュートリアルで説明したとおり、Web サイトに何らかの変更を加えるには、必ずフォームを使う必要があります (つまり、HTTP POST 操作を使う)。 リンクのクリックのみでサイトを変更できるようにした場合は (つまり、GET 操作を使う)、ユーザーがサイトに単純な要求を発行しデータを削除できます。 検索エンジン クローラーであなたのサイトのインデックスが作成されているときでさえ、リンクをたどるだけで誤ってデータが削除される可能性があります。

あなたのアプリでユーザーがレコードを変更できるようにするには、編集のためにレコードをユーザーに表示する必要があります。 しかし、レコードの削除については、この手順を省略したい場合があります。 けれども、その手順は省略しないでください。 (ユーザーがレコードを表示し、意図したレコードを削除していることを確認することも有益です。)

以降のチュートリアル セットでは、ユーザーがレコードの削除前にログインする必要があるように、ログイン機能を追加する方法を説明します。

DeleteMovie.cshtml という名前のページを作成し、そのファイルの内容を次のマークアップに置き換えます:

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

このマークアップは EditMovie ページに似ていますが、テキスト ボックス (<input type="text">) を使うのではなくマークアップに <span> 要素が含まれている点が異なります。 ここでは編集の必要はありません。 必要なのは、適切なムービーを削除していることをユーザーが確認できるように、ムービー詳細を表示することのみです。

このマークアップには、ユーザーがムービー一覧ページに戻ることができるリンクが既に含まれています。

EditMovie ページと同様に、選択したムービーの ID は非表示フィールドに格納されます。 (それは、最初にクエリ文字列値としてページに渡されます。)Html.ValidationSummary 呼び出しがあり、これにより、検証エラーが表示されます。 ここでのエラーは、ムービー ID がページに渡されなかったか、ムービー ID が正しくないという内容である場合があります。 この状況は、Movies ページでムービーを最初に選択せずにこのページを実行した場合に発生する可能性があります。

ボタンのキャプションは Delete Movie であり、その name 属性は buttonDelete に設定されています。 name 属性は、コードで、フォームを送信したボタンを特定するために使われます。

1) ページが最初に表示されたときにムービー詳細が読み取られ、2) ユーザーがボタンをクリックしたときにムービーが実際に削除されるように、コードを記述する必要があります。

1 つのムービーを読み取るコードの追加

DeleteMovie.cshtml ページの上部に、次のコード ブロックを追加します:

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

このマークアップは、EditMovie ページ内の対応するコードと同じです。 これにより、クエリ文字列からムービー ID が取得され、その ID を使ってデータベースからレコードが読み取られます。 このコードには、ページに渡されているムービー ID が有効であることを確認するための検証テスト (IsInt()row != null) が含まれています。

このコードはページの初回実行時のみ実行されることに注意してください。 ユーザーが [Delete Movie] ボタンをクリックしたときにデータベースからムービー レコードがもう一度読み取られるようにはしません。 したがって、ムービーを読み取るコードは、if(!IsPost) というテスト内にあります。つまり、要求がポスト操作 (フォーム送信) でない場合です。

選択したムービーを削除するコードの追加

ユーザーがボタンをクリックしたときにムービーが削除されるようにするには、@ ブロックの右中かっこの内側に次のコードを追加します:

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

このコードは、既存のレコードを更新するコードに似ていますが、それより簡単です。 このコードでは、基本的には、SQL の Delete ステートメントが実行されます。

EditMovie ページと同様に、このコードは if(IsPost) ブロック内にあります。 今回は、if() 条件 が少し複雑になります:

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

ここには 2 つの条件があります。 1 つ目は、前と同じで、ページが送信されていることです (if(IsPost))。

2 つ目の条件は !Request["buttonDelete"].IsEmpty() であり、要求に buttonDelete という名前のオブジェクトがあることを意味しています。 確かに、これは、どのボタンでフォームが送信されたかをテストするための間接的な方法です。 フォームに複数の送信ボタンが含まれている場合は、クリックされたボタンの名前のみが要求に含まれます。 したがって、論理的には、特定のボタンの名前が要求に含まれている場合は (または、コードで示されているように、そのボタンが空でない場合)、それが、フォームを送信したボタンです。

&& 演算子は "および" (論理 AND) を意味します。 したがって、この if 条件全体は次のようになります。

この要求はポストである (初回の要求ではない)

かつ

buttonDelete ボタンが、フォームを送信したボタンだった。

このフォーム (実際には、このページ) にはボタンが 1 つしか含まれていないため、技術的には、buttonDelete について他のテストは必要ありません。 それでも、データを完全に削除する操作を実行することにします。 そのため、その操作をユーザーが明示的に要求したときのみ実行していることを、可能な限り確認する必要があります。 たとえば、後でこのページを拡張して、他のボタンを追加したとします。 その場合にも、ムービーを削除するコードは、buttonDelete ボタンがクリックされた場合のみ実行されます。

EditMovie ページと同様に、非表示フィールドから ID を取得し、SQL コマンドを実行します。 Delete ステートメントの構文は次のとおりです:

DELETE FROM table WHERE ID = value

WHERE 句とその ID を含めることが不可欠です。 WHERE 句を除外すると、テーブル内のすべてのレコードが削除されます。 ご覧のとおり、プレースホルダーを使って ID 値を SQL コマンドに渡しています。

ムービー削除プロセスのテスト

これで、テストできるようになりました。 Movies ページを実行し、ムービーの横にある [削除] をクリックします。 DeleteMovie ページが表示されたら、[Delete Movie] をクリックします。

Delete Movie page with Delete Movie button highlighted

このボタンをクリックすると、コードによってムービーが削除され、ムービー一覧に戻ります。 そこで、削除されたムービーを検索し、それが削除されたことを確認できます。

この後の内容

次のチュートリアルでは、サイト上のすべてのページを共通の外観とレイアウトにする方法を説明します。

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

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>

その他のリソース