1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#でDBから取得した値のnullチェックにつまずいた話

Posted at

直面した問題

DBからデータを取得した際に、その値が空だった場合の処理をC#側で実装したかったのですが、以下のようなコードを書きました。(使用していたDBはSQLiteです)

id と name が格納されている users テーブルがあると想定してください。

using System;
using System.Data.SQLite;

// データベース接続・クエリ実行メソッド
public void ExecuteQuery(string query, Action<SQLiteDataReader> action)
{
    using (var conn = new SQLiteConnection("Data Source=test.db"))
    {
        conn.Open();
        using (var cmd = new SQLiteCommand(query, conn))
        using (var reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                action(reader); // readerを渡す
            }
        }
    }
}

// クエリ生成メソッド
public string SelectWhereId(int id)
{
    const int nameIndex = 0;
    string result = string.Empty;
    var query = $"SELECT name FROM users WHERE id={id}";

    // 取得後の値をラムダで渡して処理する
    ExecuteQuery(query, reader =>
    {
        var fetchedName = reader.GetString(nameIndex);
        
        // データ取得できなければその旨を出力する
        // 単純にC#のnullと比較
        if (fetchedName is null)
        {
            Console.WriteLine("name が取得できませんでした");
        }
        else
        {
            result = fetchedName;
        }
    });
    return result;
}

これでいけるやろ!と実行してみた結果、なぜか期待通りの結果にならない。
値がないはずのid で検索しても if (fetchedName is null) が true にならないわけです。

なぜnullチェックができなかったのか?

C# のSqlDataReaderでは値が取得できなかったとき、いつもよく使うnullとしては返ってきません。System.DBNullとして返ってきます。

公式ドキュメントの説明は以下。

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

このことを知らずにしばらくコードとにらめっこする羽目になりました😮‍💨

どのようにnullチェックすべきだったのか?

となると、次は C# で DB を扱う際に値が存在しなかったときはどうすればいいの?という疑問が浮かぶと思いますが、もちろんそんなことはフレームワーク内で考慮されてます。

SqlDataReaderクラスには、IsDBNull(int)というメソッドが用意されています。
このメソッドを使えば簡単に値が存在しないケースの処理が実装できます。

使い方

先ほどのメソッドを修正してみましょう。

public string SelectWhereId(int id)
{
    const int nameIndex = 0;
    string result = string.Empty;
    var query = $"SELECT name FROM users WHERE id={id}";
    ExecuteQuery(query, reader =>
    {
-       var fetchedName = reader.GetString(nameIndex);
        // データ取得できなければその旨を出力する
-       // 単純にC#のnullと比較
-       if (fetchedName is null)
+       // IsDBNullで値の存在チェック
+       if (reader.IsDBNull(nameIndex))
        {
            Console.WriteLine("name が取得できませんでした");
        }
        else
        {
-           result = fetchedName;
+           // IsDBNullがfalseの場合のみ取得処理を実行する
+           result = reader.GetString(nameIndex);
        }
    });
    return result;
}

これでOK!
初めて使うライブラリのソースはちゃんと読まなきゃダメですね...

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?