質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

GridViewのデータテーブルがnullになってしまう

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 333

chiii

score 1

前提・実現したいこと

GridViewを使用してWebアプリケーションを作成しています。
GridViewにはMySQLを使用しテーブルを表示しておりますが、データテーブルには情報が入っていないようで(GridViewには表示されているので、表示後破棄されている?)、削除ボタンが機能しません。

行の「選択」ボタンを押し、削除ボタンを押すと行が削除されるようにしたいのですが、「選択」ボタンを押したときエラーが出てしまいました。(削除ボタンはスマートタグを使用せずコーディングする予定なので、下のコードになっています。)
(System.NullReferenceException: 'オブジェクト参照がオブジェクト インスタンスに設定されていません。'dt が null でした。)

該当のソースコード

●Window1(~.aspx)

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="MySql.Data.MySqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<script runat="server">
    protected void Page_Load(Object source, EventArgs e) {
        if (IsPostBack) {
            return;
        }
        DataCatch();
    }

    DataTable dt;
    int selectRow;

    protected void Button1_Click(object sender, EventArgs e) //編集
    {
        Response.Redirect("~/edit.aspx");
    }


    protected void Button2_Click(object sender, EventArgs e) //削除
    {
        Button2.Attributes["onclick"] = "return confirm('削除しますか?');";
        DataRow dr = dt.Rows[GridView1.SelectedIndex];
        dr.Delete();
    }

    protected void Button3_Click(object sender, EventArgs e) //追加
    {
        Response.Redirect("~/edit.aspx");

    }

    private void DataCatch()
    {
        //接続文字列を取得
        string dbn = ConfigurationManager.ConnectionStrings["db"].ConnectionString;
        MySqlConnection cn = new MySqlConnection(dbn);
        cn.Open();
        //SQL文と接続情報を指定しアダプタ作成
        MySqlDataAdapter da = new MySqlDataAdapter("select * from ***", cn);
        //データ格納のためデータセット作成
        DataSet dataSet = new DataSet();
        //データ格納のためテーブル作成
        dt = new DataTable();
        dataSet.Tables.Add(dt);
        //取得
        da.Fill(dt);
        cn.Close();
        // GridViewとバインド
        GridView1.DataSource = new DataView(dt);
        DataBind();
    }

</script>
<script type="text/javascript">
    function winClose() {
        window.open('','_self').close();
    }

</script>

<html>
<body>
    <form id="form1" runat="server">
        <div>***
            <hr/>
            <br/>
            <asp:Button ID="Button1" runat="server" Text="編集" OnClick="Button1_Click" />
             <asp:Button ID="Button2" runat="server" Text="削除" OnClick="Button2_Click"/>
             <asp:Button ID="Button3" runat="server" Text="追加" OnClick="Button3_Click"/>
             <input type="button" onclick="winClose()" value="終了" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            <br />
        </div>
        <p>
            &nbsp;</p>
<asp:GridView id="GridView1" runat="server" OnRowDeleting="Button2_Click" OnSelectedIndexChanged="Button2_Click" >   
    <Columns>
        <asp:CommandField ShowSelectButton="True" />
    </Columns>
        </asp:GridView>
    </form>
</body>
</html>

補足情報(FW/ツールのバージョンなど)

ASP.NET
Visual Studio2019
C#
.NET Framework4.7.2

追記(2020.7.5)

回答を受け、修正しました。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="MySql.Data.MySqlClient" %>
<%@ Import Namespace="System.Configuration" %>
<script runat="server">
    protected void Page_Load(Object source, EventArgs e) {
        if (IsPostBack) {
            return;
        }
        DataCatch();
    }

    DataTable dt;
    int selectRow;

    protected void Button1_Click(object sender, EventArgs e) //編集
    {
        Response.Redirect("~/edit.aspx");
    }


    protected void Button2_Click(object sender, EventArgs e) //削除
    {
        int selectV = (int)GridView1.SelectedValue;
        //delete from *** where type='@selectV';
    }

    protected void Button3_Click(object sender, EventArgs e) //追加
    {
        Response.Redirect("~/edit.aspx");
    }

    private void DataCatch()
    {
        //接続文字列を取得
        string dbn = ConfigurationManager.ConnectionStrings["db"].ConnectionString;
        MySqlConnection cn = new MySqlConnection(dbn);
        cn.Open();
        //SQL文と接続情報を指定しアダプタ作成
        MySqlDataAdapter da = new MySqlDataAdapter("select * from ***", cn);
        //データ格納のためデータセット作成
        DataSet dataSet = new DataSet();
        //データ格納のためテーブル作成
        dt = new DataTable();
        dataSet.Tables.Add(dt);
        //取得
        da.Fill(dt);
        cn.Close();
        // GridViewとバインド
        GridView1.DataSource = new DataView(dt);
        DataBind();
    }
</script>

<script type="text/javascript">
    function winClose() {
        window.open('', '_self').close();
    }
</script>


<html>
<body>
    <form id="form1" runat="server">
        <div>***
            <hr/>
            <br/>
            <asp:Button ID="Button1" runat="server" Text="編集" OnClick="Button1_Click" />
             <asp:Button ID="Button2" runat="server" Text="削除" OnClick="Button2_Click" OnClientClick="return confirm('削除しますか?')"/>
             <asp:Button ID="Button3" runat="server" Text="追加" OnClick="Button3_Click"/>
             <input type="button" onclick="winClose()" value="終了" />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
            <br />
        </div>
        <p>
            &nbsp;</p>
<asp:GridView id="GridView1" runat="server" DataKeyNames="id" >   
    <Columns>
        <asp:CommandField ShowSelectButton="True" />
    </Columns>
        </asp:GridView>
    </form>
</body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • chiii

    2020/07/04 08:36

    その通りです。
    自分でコードを書くとき、いろいろと調べている段階で、イベントを追加することで削除できるのではと思考錯誤しておりました。

    キャンセル

  • SurferOnWww

    2020/07/04 08:47 編集

    基本的な問題が色々あって期待通りにはいくはずがないところに、上手くいかないからよく分からないまま色々試して、事態が悪化している感じです。

    大体やっていることは分かりましたので、後で解答欄に問題点と対応策を書いておきます。ちょっと問題が多いので書くのに時間がかかりそうです。少しお待ちください。

    キャンセル

  • chiii

    2020/07/04 08:56

    すみません。
    本当にありがとうございます。
    よろしくお願いいたします。

    キャンセル

回答 1

checkベストアンサー

0

ちょっと問題が多いので、列挙して対応案を書いておきます。その前に確認ですが対象の MySQL のテーブルには主キーはあるのですよね? 無いと何ともならないので、もし無ければ追加してください

(1) SqlDataSource を使いましょう

特に事情がなければ、SqlDataSource または ObjectDataSource と組み合わせて使うように方針変更することをお勧めします。

そうすれば、自力でコードを全く書かなくても、デザイナで MySQL のテーブルの一覧表示、編集を行うアプリを非常に簡単に作成することができます。開発工数も保守工数も激減するはずです。

方針変更による後戻り工数は発生するかもしれませんが、この先また起こるであろう問題の対応に工数を使うよりはよさそうです。

(2) Web アプリはステートレス

今のコードではポストバックされたときにはサーバー側には DataTable は存在しません。

最初の要求でサーバーが初期画面を作る際 MySQL からデータを取得し DataTable を作成して GridView にバインドしていますが、応答をブラウザに返した後は、サーバーは初期画面を作るためにメモリにロードしたもろもろ(DataTable のインスタンスを含む)をすべて消去します。

ボタンクリックでポストバックされますが、その時は DataCatch() メソッドは動かない(そういうコードになっています)ので DataTable は作られません。なのでボタンクリックでポストバックされたときは変数 dt は null です。

それでも GridView には一覧が表示されるのは ViewState に前の画面の情報が保持されていて、それを使って表示されるからです。

だからと言って、ポストバックの際も毎回 DataCatch() メソッドを動くようにして MySQL からデータを取得して DataTable を作成するのは悪手ですのでそれはやらないようにしましょう

(3) GridView の OnRowDeleting, OnSelectedIndexChanged

上手くいかないからよく分からないままいろいろ試していて、OnRowDeleting, OnSelectedIndexChanged もその一つとして追加したと理解しています。

質問文では、

行の「選択」ボタンを押し、削除ボタンを押すと行が削除されるようにしたいのですが

・・・と書いてあって、2 段階で削除するということですが、それなら不要ですので削除してください。

(4) Button2_Click の処置

dr.Delete(); は DataTable の当該行に削除マークを付けるだけです。MySQL には何の影響もなく、当然レコードの削除もできません。

上記 (3) の処置で「削除」ボタンのクリックだけでこのハンドラに制御が飛ぶようにしたら、以下のように変更してみてください。

(4-1) Button2.Attributes["onclick"] = "return confirm('削除しますか?');"; は削除。代わりに Button2 の OnClientClick プロパティに JavaScript を設定。

(4-2) GridView.SelectedValue プロパティで選択されている行の主キー値を取得できるようする。(SELECT クエリに主キーを含めることと、GridView の DataKeyNames の設定が必要)

(4-3) DataRow dr = dt.Rows[GridView1.SelectedIndex]; と dr.Delete(); も削除。代わりに GridView.SelectedValue プロパティで主キーを取得し ADO.NET + Connector/NET のコードを書いて DELETE 文を MySQL に発行し、削除する)

(4-4) GridView.DataBind() で削除後のデータを MySQL から取得して表示できるようにする。

他にも問題はいろいろありそうですが、とりあえずこれで削除はできるようになると思います。

不明点があればこの回答のコメント欄で質問してください。

【追記】

下のコメント欄の 2020/07/05 16:22 の私のコメントで、「そのためには DataCatch() メソッドのコードを直した方がよさそうです。質問にあるコードは問題もあるので、その修正案と合わせて後で回答欄に追記しておきます。」と書きましたがそれを以下に書きます。

質問者さんの DataCatch() メソッドは、(1) DataTable(または DataView)を返すように変更、(2) SELECT クエリは SELECT * FROM ... のように * は使わないで * の部分にはフィールド名を明記する、(3) 接続の Open / Close は書かないで MySqlDataAdapter に任せる(自動的に行われます)、(4) MySqlConnection の初期化のコードには using 句を使って確実に Dispose されるようにする(特に Button2_Click では必須) ・・・と言うように変更した方がよさそうです。

それを Page_Load で初期画面のデータを取得する際と、Button2_Click で削除後のデータを取得する際に GridView の DataSource プロパティに設定して、そのあとで GridView.DataBind() としてください。

以下のような感じです。

using System;
using System.Data;
using MySql.Data.MySqlClient;

namespace WebApplication1
{
    public partial class WebForm8 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                GridView1.DataSource = DataCatch();
                GridView1.DataBind();
            }
        }

        protected DataTable DataCatch()
        {
            var table = new DataTable();
            var query = "SELECT Id, xxx, yyy, zzz FROM tableA";
            var connString = "接続文字列(省略)";
            using (var connection = new MySqlConnection(connString))
            {
                var adapter = new MySqlDataAdapter();
                adapter.SelectCommand = new MySqlCommand(query, connection);
                adapter.Fill(table);
                return table;
            }
        }

        protected void Button2_Click(object sender, EventArgs e)
        {
            int selectV = (int)GridView1.SelectedValue;

            var query = "DELETE FROM tableA WHERE Id=@Id";
            var connString = "接続文字列(省略)";

            using (var connection = new MySqlConnection(connString))
            {
                using (var command = new MySqlCommand(query, connection))
                {
                    command.Parameters.Add("@Id", MySqlDbType.Int32);
                    command.Parameters["@Id"].Value = selectV;
                    connection.Open();
                    command.ExecuteNonQuery();
                }
            }

            GridView1.DataSource = DataCatch();
            GridView1.DataBind();
        }
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/05 16:22

    > GridView1.DataBind()で再表示を試みたところ、GridViewが表示されませんでした。

    回答の (4-4) で書いた「削除後のデータを MySQL から取得」というコードも書く必要があります。言葉足らずだったようですみません。

    そのためには DataCatch() メソッドのコードを直した方がよさそうです。質問にある DataCatch() メソッドのコードは少々問題があるので、その修正案と合わせて後で回答欄に追記しておきます。

    キャンセル

  • 2020/07/05 16:31

    何から何まで本当にありがとうございます。よろしくお願いいたします。

    キャンセル

  • 2020/07/05 17:14

    ありがとうございます。
    選択行が削除され、GridViewの削除後の状態を表示できました。
    これからコードを読み解いていきます。ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る