まえおき
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;
今日はここまで。