datagridviewのエラー処理に関して

タグの編集
投稿者 T  (社会人) 投稿日時 2020/3/11 09:39:48
お世話になります。
現在DataGridViewとOracleのデータベースを繋げて簡単な表示ができるアプリケーションを作成しています。そこで、主キーを入力しなかったり主キーを重複し記入してしまった場合、エラーを知らせる任意のメッセージボックスを表示できるようにしたいと思っています。最初は、自分で作ったボタンを押すとテーブルアダプタのアップデートメソッドが働き、それぞれの例外が出てきたのでそれに対してCatch、Tryで対応しようと思ったのですが、「Data Error イベントをハンドルしてください」と表示されてしまいました。ここからどう進めばよいかわかりません。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/11 09:54:24
DataSet の EnforceConstraints プロパティをオフにした状態であれば、
その下の DataTable が制約違反な状態…たとえば主キー重複があっても
受け入れるようになります。
https://docs.microsoft.com/ja-jp/dotnet/framework/data/adonet/dataset-datatable-dataview/datatable-constraints


EnforceConstraints プロパティを戻せばエラー判定が再開されて
例外として通知されます。

どの行でエラーになっているのかは、DataTable の GetErrors メソッドで得られます。
行にエラーがあるかは、DataRow の HasErrors プロパティで得られます。
行内のどの列がエラー要因なのかは、DataRow の GetColumnsInError メソッドで得られます。
エラー内容は、DataRow の GetColumnError メソッドで得られます。


DataGridView については、ShowCellErrors / ShowRowErrors プロパティの指定により、
こうしたエラー状態を表示するかどうかを制御できます。

DataGridView にエラーアイコンを表示する方法は、下記が参考になるかと思います。
https://dobon.net/vb/dotnet/datagridview/showcellerrors.html


> 自分で作ったボタンを押すとテーブルアダプタのアップデートメソッドが働き、
型付DataSet をお使いなのですね。
Update メソッドを呼びだす前に、入力内容の制約チェックを実施しておきましょう。


> 「Data Error イベントをハンドルしてください」と表示されてしまいました。
Data Error イベントではなく、
DataError イベントですね。

DataGridView の DataError イベントでは、引数 e から、
問題となった行・列・例外内容を拾うことができます。
そして、e.ThrowException と e.Cancel の組み合わせにより、
例外を握りつぶすのか、そのまま例外処理を続けるのかを指示できます。
投稿者 T  (社会人) 投稿日時 2020/3/11 10:33:41
迅速な対応ありがとうございます。本当に助かります。

その後、DataErrorハンドルを見つけ以下のようにコーディングしてみました。


 Private Sub WORKDataGridView_DataError(ByVal sender As Object, ByVal e As DataGridViewDataErrorEventArgs) Handles WORKDataGridView.DataError
        Try
            e.ThrowException = True

        Catch ex As System.Data.NoNullAllowedException
            Dim noID As String = "IDが入力されていません。"
            MessageBox.Show(noID)

        Catch ex As System.Data.ConstraintException
            Dim saID As String = "IDが重複しています。"
            MessageBox.Show(saID)

        End Try

    End Sub



ただこれだと、それぞれの例外がcatchされず、ThrowExceptionの後にすぐさまEndTryに移行してしまいます。
投稿者 (削除されました)  () 投稿日時 2020/3/11 13:02:33
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/11 13:06:31
「e.ThrowException = True」という代入操作は例外を発生させないので、
御提示のコードで Catch 句が呼ばれることは無いですよ。


e.ThrowException は、「イベントを抜けた後で、例外を発生させるかどうか」を意味しています。
True をセットしておいた場合は、例外がスローされます。

False をセットしておいた場合、エラーメッセージは出ないので、
 そのままエラーメッセージを握りつぶす
 MessageBox 等を使って表現する
 Label 等にエラー情報を記述する
 該当セルの ErrorTextプロパティにエラーテキストをセットする
などのようにして、自前の復帰処理に差し替えてください。


実際のエラー内容は、e.Exception という読み取り専用プロパティにて保持されています。
エラーが発生した時の状態は e.Context が返すビットフラグで判定できます。

また、e.Cancel を True に変更することで、入力中の内容をキャンセルして、
編集前の状態に戻すこともできます。
https://dobon.net/vb/dotnet/datagridview/dataerror.html
投稿者 T  (社会人) 投稿日時 2020/3/11 13:46:03
ありがとうございます。例外自体が発生していなかったんですね。

ちなみに、先述のコードにおいてCatch句を呼ぶようにするにはどういった記述が必要になるんでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/11 14:02:25
何故 Catch 句を使いたいのでしょうか?

「Catch ex As HogeException When 何某」で拾うのではなく、
「ElseIf TypeOf e.Exception Is HogeException AndAlso 何某 Then」等では駄目ですか?

※When 句無しの場合も同様。


どうしても例外を再スローしたいというのであれば、一応、
 '要 .NET Framework 4.5 以上
 ExceptionDispatchInfo.Capture(e.Exception).Throw()
と書けなくも無いですが…。
投稿者 T  (社会人) 投稿日時 2020/3/11 15:33:05
謎のこだわりに見えてしまったようで申し訳ありません。
というのも、当方プログラミング未経験者でして、
練習としてTry、CatchやIfなどの基本的な制御構文を用いた例外処理をしてみたかったというのが本音です。ただ利用しているのが.NET Framework 4.0なので無理そうですが…。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/3/11 18:16:08
> 基本的な制御構文を用いた例外処理をしてみたかったというのが本音です。

手段と目的が入れ替わっちゃってましたね…。😅


> ただ利用しているのが.NET Framework 4.0なので無理そうですが…。

.NET Framework 4 のサポートは、既に 2016/01/12 をもって終了しています。

折角なら現行バージョンのものを学習されることをお奨めします。
作成したアプリを他の人に配布する場合にも、その方が都合が良いでしょう。

ちなみに
  Windows 8.1 なら .NET Framework 4.5.1 以上
  Windows 10 v1803/v1809 なら .NET Framework 4.7.2 以上
  Windows 10 v1903/v1909 なら .NET Framework 4.8 以上
が既定でインストールされています。


開発環境のサポート期限はこんな感じ。
 2020/07/14 … Visual Studio 2010 Service Pack 1 のサポートが終了
 2023/01/10 … Visual Studio 2012 Update 4 のサポートが終了
 2024/04/09 … Visual Studio 2013 Update 3 のサポートが終了
 2024/10/14 … Visual Studio 2015 Update 3 のサポートが終了
 2027/04/03 … Visual Studio 2017 のサポートが終了(v15.9.x)
 2029/04/10 … Visual Studio 2019 のサポートが終了


> .NET Framework 4.0なので無理そうですが…。

.NET Framework 4 には、e.Exception のインスタンスを意図的に
Try ブロックで再スローするための標準的な方法が用意されていません。

エラー発生個所の情報(スタックトレース)を失っても構わないのであれば、
    Throw e.Exception
と書くことで疑似再スローが行えますが、デメリットしか無いですね。
素直に If 文で捕らえましょう。

なお、Try ブロックではなく Catch ブロックの中で再スローする場合には、
引数なしで Throw ステートメントを呼び出せば OK です(今回の目的には合致しませんが)。
こちらは .NET Framework のバージョンには依存しません。
    Throw
投稿者 T  (社会人) 投稿日時 2020/3/13 09:23:30
ありがとうございます。
仰る通り、最新の.NETのバージョンで試してみようと思います。
本当にありがとうございました。