【C#】DataRowの値を取り出すときにDBNullでエラーを出さないようにするには? DBNull.Value と null の違い、エラーの回避方法について
DataSetからDB値を取得して文字型の変数にセットする際、DBNULL値が型変換できなくてエラーになります。
DBNULL値の場合の対応方法について学びましょう。
まえおき

DBNULL値を取得した場合の対応として、事前の空値チェックで「if (row["**"] == null){…}」と記述していました。
ただ、この場合取得したデータがDBNULL値だとnullと比較できないんです。

DBNULLとnullってどう違うかお分かりでしょうか?
また、合わせてDBNULLエラーを出さない方法について考えます。

nullとなぜ比較できないの?

具体例をあげます。 DataRowから値を読み込むとき、 たとえば、

// データテーブルクラスのインスタンス生成
DataTable dt = new DataTable();

// データカラムクラスのインスタンス生成(カラム名、属性)
DataColumn dc_name = new DataColumn("name", typeof(String));

// 作成したデータテーブルに、作成したデータカラムを追加
dt.Columns.Add(dc_name);

// 作成したデータテーブル(カラム名、属性つき)に、新しい行を作成
DataRow dr = dt.NewRow();


と適当にDataRowを用意して、

String name = (String)dr["name"];

と読み取ろうとすると、
「型 'System.InvalidCastException' のハンドルされていない例外が mscorlib.dll で発生しました」
「追加情報:オブジェクトを DBNull から他のタイプにキャストすることはできません。」
と叱られます。

これは、列名を添え字にしてアクセスし、フィールドの値を取得しています(dr["id"])が、 フィールドの値がNULLの場合、値が「DBNull.Value」だからです。
そこで、dr["id"]の値がNullかどうかを調べる必要があるわけですが、if (dr["id"]!=null) とやっても、 dr["id"]そのものはNullではないのでうまく行きません。

nullとDBNull.Valueの違いは何?

オブジェクト指向プログラミング言語でのnullの概念と、DBNullオブジェクトは異なります。

オブジェクト指向プログラミング言語では、nullはオブジェクトへの参照がないことを意味します。
DBNullは、初期化されていないバリアントまたは存在しないデータベース列を表します。

なぜnullではなく、DBNull.Valueが返ってくるのか?

DBNull.Valueは、.NETデータベースプロバイダがデータベースにnullエントリを表すために返すものです。
nullとは完全に異なり、フィールド値にNULLが含まれている場合に返される定数です。

DBNull.Value は、Object 型変数にしかセットできません。
DataRow は Object 型なので、セットすることができます。

エンジンでは、比較の際に DBNull 値が Null に変換され、データベースに挿入する際に Null が DBNull 値に変換されます。
なので、DBから値を取得した際は、DBNull.Valueになります。

DBNull.Valueのエラーを回避する方法は?

例)
1.DBNull.Valueと比較する。
2.DataRow項目のIsNullメソッドを使用する。
3.DataRowなどのObject型クラス(変数)に値をセットする。※

※.NETクラスで、戻り値の型がない場合は、オブジェクト型になります。
値型以外、参照型のパラメーターには Null をパラメーターとして渡せますが、 メンバーの実装によっては実行時エラーが発生する場合があります。

1.DBNULL.Valueと比較する例

// データテーブルに列を追加
DataTable dataTable = new DataTable();
dataTable.Columns.Add("col_String", typeof(System.String));

// -------------------------------------------------------
// NULLを設定
// -------------------------------------------------------
DataRow row = dataTable.NewRow();

// NG例
row["col_String"] = null;

// OK例
row["col_String"] = DBNull.Value;

// -------------------------------------------------------
// NULLの判定
// -------------------------------------------------------

// OK例
if (row["col_String"] == DBNull.Value)
{
   // 表示される
   MessageBox.Show("DBNull.Valueです。");
}

// NG例
if (row["col_String"] == null)
{
   // 表示されない
   MessageBox.Show("nullです。");
}


2.DataRow項目のIsNullメソッドを使用する例

if (row.IsNull("col_String"))
{
   // 表示される
   MessageBox.Show("IsNULLです。");
}

3.DataRowなどのObject型クラス(変数)に値をセットする例

Datatable dataTable =  new DataTable();

// データテーブルに列を追加
dataTable.Columns.Add("col_String", typeof(System.String));
DataRow row = dataTable.NewRow();
row["col_String"] = DBNull.Value;

//方法1
string x = row["col_String"];

//方法2
string x = dataTable.Rows[0]["col_String"];

列名["col_String"]の値は、DBNull.Valueでオブジェクト型になります。
Stringクラスの変数は、Object型を継承しているのでDBNull.Valueをキャストせず、そのまま格納できます。

NULL値を持つ値を含めて、列名["col_String"]の値をstring型に変換したい場合は、

// NG例
// DBNull.Value が格納されていた場合 InvalidCastException がスローされる
string x = (string)dataTable.Rows[0]["col_String"];

// OK例
string x =
DBNull.Value.Equals(dataTable.Rows[0]["col_String"]) ? null
  : (string)dataTable.Rows[0]["col_String"]);

のようにDBNULLとまず比較して、DBNULLの場合は"null"をstring型にキャストします。

データベースのフィールドのNULLはstring型にはキャストできないため、エラーが発生しますが、 "null"のstring型へのキャストは、エラーは起きません。
※数値型の変数(int, doubleなど)に代入する場合は、null許容型で宣言することでnullを代入できます。
例)
string x = null;
int? x = null;

今日はここまで。