C#でDBNullチェック時にエラーを出さずにキャストする最適解

C#でDBのデータを取得する際に厄介なのがDBNull。。

DBNullはNullとは別物のため、Null判定できません。

故に三項演算子などを使ってDBNull回避する必要があります。

そこでDBNullをうまく回避しつつ、同時にキャストしてくれる関数を作れないか?と考えたところ。

私の中での最適解が生まれたので書いていきます。

        public static T Convert<T>(object obj)
        {
            if (obj != DBNull.Value && obj != null)
            {
                return (T) obj;
            }

        return default;
    }</code></pre></div>

object型は何でも受け取ることができます。

なので一度Object形で受け取り、DBNullとNullでないことをチェックします。

DBNullでもNullでもなければ、指定の型にキャストします。

Nullだった場合Defaultを返します。

これでDBNullをきれいに回避することができるのではないでしょうか??

この関数の使い方は下記のようになります。(SqlDataReaderで書いていきますが、DataRowでも使えると思います。)

                    using (var dataReader = command.ExecuteReader())
                    {
                        if (dataReader.HasRows)
                        {
                            while (dataReader.Read())
                            {
                               var username = Convert<string>(reader["UserName"]);
                               var userId = Convert<string>(reader["UserId"]);
                               var userpass = Convert<string>(reader["UserPass"]);
                               var roleId = Convert<int>(reader["RoleId"]);
                            }
                        }
                    }

キャストしたい型を指定して、引数にreaderのItemを投げればうまくできますね!

拡張メソッドを使えば下記のようにできますが、すべての型で拡張されてしまうので、推奨はしません。

拡張メソッドで書いてみる

💡 DataAdapter.Update で DBNull で例外が出るのは保守の現場あるある。 C# DataAdapter.Update() で DBNull 例外が出た時の最短対処 で同じ系統のハマりを潰してます。

    public static class ObjectHelper
    {
        public static T ReplaceDBNull<T>(this object obj)
        {
            if (obj != DBNull.Value && obj != null)
            {
                return (T) obj;
            }

        return default;
    }
}</code></pre></div>

こんな感じで書くと以下のような形で使えます。

                    using (var dataReader = command.ExecuteReader())
                    {
                        if (dataReader.HasRows)
                        {
                            while (dataReader.Read())
                            {
                               var username = reader["UserName"].ReplaceDBNull<string>();
                               var userId = reader["UserId"].ReplaceDBNull<string>();
                               var userpass = reader["UserPass"].ReplaceDBNull<string>();
                               var roleId = reader["RoleId"].ReplaceDBNull<int>();
                            }
                        }
                    }

この方法は推奨はしません。

何故ならObject型はすべての型が継承している型になっています。

それすなわち全ての型にReplaceDBNullが実装されるということになります。

個人開発ならまだいいかもしれませんが、みんなで使う際は避けたほうがいいかもしれませんね。

目次

💡 補足: 業務系の現場でよくハマるパターン

俺もこの DBNull のチェック処理、客先で何度も書いてきましたが、3つの罠は今でも油断すると踏みます。

① DBNull と null の混在で if 文が機能しない

if (row["col"] == null) は DBNull で来た値には false 判定なので、 row["col"] is DBNull or row.IsNull("col") を使う必要があります。レガシーコードで != null チェックが効かないバグの定番原因。 Linq で Null 回避する書き方とパフォーマンス でも同じ罠を扱ってます。

② 値型 (int, DateTime) で DBNull キャスト失敗

(int)row["col"] で DBNull が来ると InvalidCastException。Null 許容型で受けるか、 row.Field("col") の Field 拡張メソッドを使うのが安全です。今回の拡張メソッドはまさにこれを楽にする実装ですね。

③ DataRow["col"] の文字列キー指定でタイポ → 実行時例外

列名のスペルミスは IDE で気づけない。実行時に IndexOutOfRangeException で初めて発覚する。型付き DataSet を使うか、定数化するのが業務系での自衛策。DataGridView × DataTable でバインディングする時もこの罠に注意。

❓ よくある質問

Q1. DBNull と null の違いは?

A. DBNull は「DB の NULL を C# 側で表すための特殊値」、null は「.NET の参照型がない状態」。DataTable から取り出した時の値が DBNull、変換後に保持する変数が null、と思うと頭が整理されます。 DataAdapter で Fill と Update でも DBNull の取り回しを扱ってます。

Q2. 値型 (int? DateTime?) でのキャストはどう書く?

A. row.Field("col") が最短。Null 許容型を使えば DBNull が null に自動変換される。Field 拡張メソッドが業務系の DataTable 操作で最強の友です。

Q3. Linq で DBNull を含む列を扱うには?

A. dt.AsEnumerable().Where(r => !r.IsNull("col")) で DBNull 行を除外、または r.Field("col") ?? 0 でデフォルト値に倒す。 DataTable × Linq の3パターン で具体的なコード例を出してます。

Q4. SQL Server の NULL 列を判定するには?

A. SQL 側で ISNULL(col, デフォルト)COALESCE で潰すのが定石です。C# 側でチェックするより SQL 側で潰す方が抜け漏れが減る。レガシー保守だと両側でチェックしてる二重防御パターンもよく見ます。

Q5. EF Core ならどうハンドリング?

A. EF Core はプロパティ型を nullable にしておけば自動で DBNull → null に変換してくれます。DBNull を意識する必要がほぼないのが嬉しい。レガシー DataAdapter からの移行で「DBNull 地獄から解放される」のは EF Core のメリットの1つ。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次