DataRowから値を読み込むとき、 たとえば、
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を用意して、
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を使って、DataRowのカラムの値がnullなら、三項演算子で任意の値を入れるようにします。
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を返します。
でも、あんまりスマートな書き方でないので、調べたら .Net Framework3.5からこんな書き方ができるようになっていました。
UInt64 id = dr.Field("id") ?? 0; String name = dr.Field("name") ?? ""; Boolean chk = dr.Field("chk") ?? false;
dr.Field(“id”) で、dr[“id”]の中身を取り出してくれます。
UInt64? の ? は、Nullable型とすることを表して、Null非許容型に対してNullを入れられるようにします。String はもともとNull許容型なので、?はつけません。
?? はNullかどうかを判定する演算子で、Nullなら、別の値をいれることができます。
なので、 UInt64 id = dr.Field(“id”)??0; は、dr[“id”]の内容がDBNullならnullとし、その場合0をidに入れる、ということになります。
さらに、defaultで初期値を取得すれば、書き方を定型化できます。
UInt64 id = dr.Field("id") ?? default(UInt64); String name = dr.Field("name") ?? default(String); Boolean chk = dr.Field("chk") ?? default(Boolean);
いちいちFieldを使うのも面倒なので、個人的には次のように関数化してつかってます。
public static Type ConvertDataRow(DataRow dr, String columnName) { Type ret = (dr.IsNull(columnName)) ? default(Type) : dr.Field(columnName); return ret; }
こんな感じで使います。
UInt64 id = ConvertDataRow(dr, "id"); String name = ConvertDataRow(dr, "name"); Boolean chk = ConvertDataRow(dr, "chk");
ちなみに、MySQLのtinyint型の場合は、byteとして取り込まれるので、つぎのようにして取得します。
int status = Common.Datas.ConvertDataRow(dr2, "status");