DataRowから値を読み込むとき、 たとえば、
1 2 3 4 5 6 7 8 9 |
DataTable dt = new DataTable(); DataColumn dc_id = new DataColumn("id", typeof(UInt64)); dt.Columns.Add(dc_id); DataColumn dc_name = new DataColumn("name", typeof(String)); dt.Columns.Add(dc_name); DataColumn dc_chk = new DataColumn("chk", typeof(Boolean)); dt.Columns.Add(dc_chk); DataRow dr = dt.NewRow(); |
と適当にDataRowを用意して、
1 2 3 |
UInt64 id = (UInt64)dr["id"]; String name = (String)dr["name"]; Boolean chk = (Boolean)dr["chk"]; |
と読み取ろうとすると、
「型 ‘System.InvalidCastException’ のハンドルされていない例外が mscorlib.dll で発生しました」
「追加情報:オブジェクトを DBNull から他のタイプにキャストすることはできません。」
と叱られます。
これは、dr[“id”]などのDataRowの値がNullだからです。
そこで、dr[“id”]の値がNullかどうかを調べる必要があるわけですが、if (dr[“id”]!=null) とやっても、dr[“id”]そのものはNullではないのでうまく行きません。
dr[“id”]の値がNullかどうかは、if (!dr.IsNull(“id”)) とか、if (dr!=DBNull.Value) のようにするといいわけですが、IF文を大量に並べるのはちょっと見苦しいですよね。
DBNull.Value.Equalsと三項演算子で処理する
そこで、DBNull.Value.Equalsを使って、DataRowのカラムの値がnullなら、三項演算子で任意の値を入れるようにします。
1 2 3 |
UInt64 id = DBNull.Value.Equals(dr["id"]) ? 0 : (UInt64)dr["id"]; String name = DBNull.Value.Equals(dr["name"]) ? "" : (String)dr["name"]; Boolean chk = DBNull.Value.Equals(dr["chk"]) ? false : (Boolean)dr["name"]; |
とすると、DBNullのときでもエラーが出ません。 DBNull.Value.Equals は、内容DBNullかどうかをチェックしてDBNullならTrueを返します。
DataRow.Fieldとnull 合体演算子で処理する
でも、あんまりスマートな書き方でないので、調べたら .Net Framework3.5からこんな書き方ができるようになっていました。
1 2 3 |
UInt64 id = dr.Field<UInt64?>("id") ?? 0; String name = dr.Field<String>("name") ?? ""; Boolean chk = dr.Field<Boolean?>("chk") ?? false; |
dr.Field
UInt64? の ? は、Nullable型とすることを表して、Null非許容型に対してNullを入れられるようにします。String はもともとNull許容型なので、?はつけません。
?? はNullかどうかを判定する演算子で、Nullなら、別の値をいれることができます。
なので、 UInt64 id = dr.Field
さらに、defaultで初期値を取得すれば、書き方を定型化できます。
1 2 3 |
UInt64 id = dr.Field<UInt64?>("id") ?? default(UInt64); String name = dr.Field<String>("name") ?? default(String); Boolean chk = dr.Field<Boolean?>("chk") ?? default(Boolean); |
DataRowの値を取得する関数を作る
いちいちFieldを使うのも面倒なので、個人的には次のように関数化してつかってます。
1 2 3 4 5 |
public static Type ConvertDataRow<Type>(DataRow dr, String columnName) { Type ret = (dr.IsNull(columnName)) ? default(Type) : dr.Field<Type>(columnName); return ret; } |
こんな感じで使います。
1 2 3 |
UInt64 id = ConvertDataRow<UInt64>(dr, "id"); String name = ConvertDataRow<String>(dr, "name"); Boolean chk = ConvertDataRow<Boolean>(dr, "chk"); |
ちなみに、MySQLのtinyint型の場合は、byteとして取り込まれるので、つぎのようにして取得します。
1 |
int status = Common.Datas.ConvertDataRow<byte>(dr2, "status"); |