SQLデータリーダー-Null列値の処理


297

SQLdatareaderを使用して、データベースからPOCOを構築しています。コードは、データベースでnull値を検出した場合を除いて機能します。たとえば、データベースのFirstName列にnull値が含まれている場合、例外がスローされます。

employee.FirstName = sqlreader.GetString(indexFirstName);

この状況でnull値を処理する最良の方法は何ですか?

回答:


470

次の点を確認する必要がありますIsDBNull

if(!SqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

これが、この状況を検出して処理するための唯一の信頼できる方法です。

これらを拡張メソッドにラップし、列が実際にある場合はデフォルト値を返す傾向がありますnull

public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
   if(!reader.IsDBNull(colIndex))
       return reader.GetString(colIndex);
   return string.Empty;
}

これで、次のように呼び出すことができます。

employee.FirstName = SqlReader.SafeGetString(indexFirstName);

また、例外やnull値について再び心配する必要はありません。


64
誰かがインデックスではなく列名を必要とする場合、次のことを実行できます。int colIndex = reader.GetOrdinal(fieldname);@ marc_sのSafeGetString関数を簡単にオーバーロードします。
ilans

劣らVBで、私は2019年にここにいると信じてすることはできません..........おかげものの、大きな助け
JimmyB

次のように行うこともできます。int ordinal = reader.GetOrdinal( "col_name"); uint?val = reader.IsDBNull(ordinal)?(uint?)null:reader.GetUInt32(ordinal);
ed22

こんにちは!コピーしてフォームに貼り付けようとしたところ、エラーが返されました。「拡張メソッドは、非ジェネリック静的クラスで定義する必要があります。」
Jansen Malaggay

SafeGetString内でreader.GetOrindalを使用していて、ループ内でSafeGetStringを使用したい場合、パフォーマンスに影響を与えずにこれをどのように実現できますか?
AspUser7724

223

デフォルト値にasは、演算子と組み合わせた演算子を使用する必要があり??ます。値型はnull許容として読み取られ、デフォルトが指定されている必要があります。

employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);

asオペレータはがDBNullのチェックなど、キャストを処理します。


6
誰かがAge列をintからSQL bigint(c#long)に変更した場合、コードは0を返すことでサイレントに失敗します。ZXXからの答えは、より信頼できるIMOです。
MartinØrding-Thomsen、2011

default(int)を0ではなく-1にオーバーライドできるかどうか疑問に思います
Chris

5
@Chris-default(int)を-1で置き換えることができるはずです。
stevehipwell 2013年

@ Stevo3000あなたは正しいです!私はそれを試してみましたが、投稿した直後に言ったように機能しましたが、このページに戻るのを忘れていました:)
Chris

5
ここで "as"を使用すると、インデックスエラーを隠すことができることに注意してください。誤ってを使用sqlreader[indexAge] as string ?? ""した場合は、常にが取得され""ます。本当に必要かどうかを検討して(int?)sqlreader[indexAge] ?? defaultValueください。SQLが変更されると、不正な値ではなく例外が発生します。@ Stevo3000:default(int)は-1ではなく0です。@Chris:本当に必要なものを使用していることを確認してください。
me22 2013年

30

文字列の場合は、オブジェクトのバージョン(配列演算子を使用してアクセス)をキャストし、nullの場合はnull文字列で終了できます。

employee.FirstName = (string)sqlreader[indexFirstName];

または

employee.FirstName = sqlreader[indexFirstName] as string;

整数の場合、null許容intにキャストすると、GetValueOrDefault()を使用できます

employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();

またはnull結合演算子(??)。

employee.Age = (sqlreader[indexAge] as int?) ?? 0;

2
最初の例のように、明示的なキャストは機能しません。同じエラーが発生する
musefan 2017

@musefan:フィールドは実際には文字列ですか?そうでない場合は、別のエラーが発生します。これは機能し、これらは例1と例2(構文を除いて)の実際の違いではありません。
Gone Coding

1
@GoneCoding:はい、これはnull可能文字列であり、2番目の文字列が機能するときに最初の文字列が問題を引き起こすことは間違いありません。問題はnull値がどのように処理されるかによって引き起こされると思います。同様に、それらはnullDBNullオブジェクトではなく、DBNullオブジェクトです。2つのステートメントの違いは、最初のステートメントが文字列でない場合は失敗し、2番目のステートメントは文字列でない場合はnullを返すだけです。
musefan 2017

23

IsDbNull(int)通常、のようなメソッドを使用GetSqlDateTimeしてから比較するよりもはるかに遅くなりますDBNull.Value。これらの拡張メソッドを試してくださいSqlDataReader

public static T Def<T>(this SqlDataReader r, int ord)
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return default(T);
    return ((INullable)t).IsNull ? default(T) : (T)t;
}

public static T? Val<T>(this SqlDataReader r, int ord) where T:struct
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? (T?)null : (T)t;
}

public static T Ref<T>(this SqlDataReader r, int ord) where T : class
{
    var t = r.GetSqlValue(ord);
    if (t == DBNull.Value) return null;
    return ((INullable)t).IsNull ? null : (T)t;
}

次のように使用します。

var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);

5
System.Data.SqlTypes型の明示的な演算子がこのコードを使用しようとすると、どこにでもエラーがスローされることを発見しています...
Tetsujin no Oni

これが時々失敗する理由の説明については、stackoverflow.com / a / 21024873/1508467を参照してください(Val <int>を使用してSQL int列を読み取ってみてください)。
Rhys Jones、

12

これを行う1つの方法は、db nullをチェックすることです。

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));

12

reader.IsDbNull(ColumnIndex) 多くの答えが言うように動作します。

列名を操作する場合は、型を比較す​​るだけの方が快適な場合があります。

if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }

これはSystem.Dataと.NET FWの古いバージョンでも機能します
RaSor

11

列名を使用してデータリーダー内で行が返されるとき、NULL列値はないと思います

これを行うdatareader["columnName"].ToString();と、常に空の文字列になる可能性のある値が提供されます(String.Empty比較する必要がある場合)。

私は以下を使用し、あまり心配しません:

employee.FirstName = sqlreader["columnNameForFirstName"].ToString();

4
reader [FieldName] == DBNull.Valueを実行して、NULLをチェックできます
Ralph Willgoss

11

このソリューションはベンダー依存性が低く、SQL、OleDB、およびMySQLリーダーで動作します。

public static string GetStringSafe(this IDataReader reader, int colIndex)
{
    return GetStringSafe(reader, colIndex, string.Empty);
}

public static string GetStringSafe(this IDataReader reader, int colIndex, string defaultValue)
{
    if (!reader.IsDBNull(colIndex))
        return reader.GetString(colIndex);
    else
        return defaultValue;
}

public static string GetStringSafe(this IDataReader reader, string indexName)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName));
}

public static string GetStringSafe(this IDataReader reader, string indexName, string defaultValue)
{
    return GetStringSafe(reader, reader.GetOrdinal(indexName), defaultValue);
}

1
このコードを直接拡張クラスに直接コピーしてカスタマイズしています。
qxotk 2016

8

SELECTステートメントのnull値を適切なものに置き換える傾向があります。

SELECT ISNULL(firstname, '') FROM people

ここでは、すべてのnullを空の文字列に置き換えます。その場合、コードはエラーになりません。


可能であれば、これを使用してnullを回避します。それ以外の場合は、ヘルパーメソッドのソニーボーイの答えが好きです。
払い戻しなし返品不可

3
なぜ静的な別のヘルパーメソッドですか?SqlDataReaderの拡張メソッドは、より魅力的で直感的に見えませんか?
marc_s 2009年

7

ジェネリック関数を記述してヌルをチェックし、ヌルの場合はデフォルト値を含めることができます。Datareaderを読み取るときにこれを呼び出します

public T CheckNull<T>(object obj)
        {
            return (obj == DBNull.Value ? default(T) : (T)obj);
        }

データリーダーを読むとき

                        while (dr.Read())
                        {
                            tblBPN_InTrRecon Bpn = new tblBPN_InTrRecon();
                            Bpn.BPN_Date = CheckNull<DateTime?>(dr["BPN_Date"]);
                            Bpn.Cust_Backorder_Qty = CheckNull<int?>(dr["Cust_Backorder_Qty"]);
                            Bpn.Cust_Min = CheckNull<int?>(dr["Cust_Min"]);
                         }


4

ヘルパーメソッドの作成方法

文字列の場合

private static string MyStringConverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return "";

        return o.ToString();
    }

使用法

MyStringConverter(read["indexStringValue"])

Int

 private static int MyIntonverter(object o)
    {
        if (o == DBNull.Value || o == null)
            return 0;

        return Convert.ToInt32(o);
    }

使用法

MyIntonverter(read["indexIntValue"])

日付

private static DateTime? MyDateConverter(object o)
    {
        return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
    }

使用法

MyDateConverter(read["indexDateValue"])

注:DateTimeの場合、varialbeを次のように宣言します。

DateTime? variable;

4

marc_sによる回答への追加として、より一般的な拡張メソッドを使用して、SqlDataReaderから値を取得できます。

public static T SafeGet<T>(this SqlDataReader reader, int col)
    {
        return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
    }

Tが構造体の場合、nullをTのnull以外のデフォルトに変換するため、このメソッドを「SafeGet」とは呼びません。実際には安全ではありません。おそらく「GetValueOrDefault」でしょう。
Rhys Jones

@RhysJonesこの場合、構造体としてTを使用するのはなぜですか。たとえそうであっても、構造体のnull以外のデフォルトは期待される動作であると主張します。
getpsyched

@RhysJonesしかし、このメソッドは安全ではない可能性があることに同意します。SqlDataReaderからのInvalidCastExceptionなどの例外を処理する必要があります。
getpsyched

4

getpsychedの回答に影響を与えることで、列の値を名前でチェックするジェネリックメソッドを作成しました

public static T SafeGet<T>(this System.Data.SqlClient.SqlDataReader reader, string nameOfColumn)
{
  var indexOfColumn = reader.GetOrdinal(nameOfColumn);
  return reader.IsDBNull(indexOfColumn) ? default(T) : reader.GetFieldValue<T>(indexOfColumn);
}

使用法:

var myVariable = SafeGet<string>(reader, "NameOfColumn")

私はあなたが誰であるかはわかりませんが、あなたを祝福します。この機能により、3時間以上の作業時間を節約できました。
Ramneek Singh


2

一連の静的メソッドを使用して、データリーダーからすべての値を引き出します。したがって、この場合は次のように呼び出しDBUtils.GetString(sqlreader(indexFirstName)) ます。静的/共有メソッドを作成する利点は、同じチェックを何度も繰り返す必要がないことです...

静的メソッドには、nullをチェックするコードが含まれます(このページの他の回答を参照してください)。


2

条件演算子を使用できます。

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";

以下の回答の1つと同じですが、8年遅れます。
ビール

2

ここにはたくさんの答えがあり、有用な情報(およびいくつかの間違った情報)が広がっています。それらをすべてまとめたいと思います。

質問への短い答えは、DBNullをチェックすることです-ほとんど誰もがこのビットに同意します:)

ヘルパーメソッドを使用してSQLデータ型ごとにnull許容値を読み取る代わりに、ジェネリックメソッドを使用すると、はるかに少ないコードでこれに対処できます。ただし、null許容値の型と参照型の両方に単一のジェネリックメソッドを使用することはできません。これについては、null許容型で汎用パラメーターとして詳しく説明してい ますか?そしてnull可能すべてのためのC#ジェネリック型制約

したがって、@ ZXXと@getpsychedからの回答に続き、null可能値を取得するための2つのメソッドと、null以外の値の3番目を追加しました(メソッドの名前付けに基づいてセットを完成させます)。

public static T? GetNullableValueType<T>(this SqlDataReader sqlDataReader, string columnName) where T : struct
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? (T?)null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNullableReferenceType<T>(this SqlDataReader sqlDataReader, string columnName) where T : class
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.IsDBNull(columnOrdinal) ? null : sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

public static T GetNonNullValue<T>(this SqlDataReader sqlDataReader, string columnName)
{
    int columnOrdinal = sqlDataReader.GetOrdinal(columnName);
    return sqlDataReader.GetFieldValue<T>(columnOrdinal);
}

私は通常、列名を使用します。列インデックスを使用する場合は、これらを変更してください。これらのメソッド名に基づいて、データがnull可能であることを期待しているかどうかを確認できます。これは、かなり前に記述されたコードを見るときに非常に役立ちます。

チップ;

  • データベースにnull入力可能な列がないことで、この問題を回避できます。データベースを制御できる場合、列はデフォルトで非ヌルであり、必要な場合にのみヌル可能にする必要があります。
  • C#の 'as'演算子を使用してデータベース値をキャストしないでください。キャストが間違っていると、暗黙的にnullが返されます。
  • デフォルト値式を使用すると、int、datetime、bitなどの値タイプのデータベースnullがnull以外のに変更されます。

最後に、上記のメソッドをすべてのSQL Serverデータ型でテストしている間、SqlDataReaderから直接char []を取得することはできません。char[]が必要な場合は、文字列を取得してToCharArray()を使用する必要があります。


1

以下のコードを使用して、データテーブルに読み込まれるExcelシートのnullセルを処理しています。

if (!reader.IsDBNull(2))
{
   row["Oracle"] = (string)reader[2];
}

1
private static void Render(IList<ListData> list, IDataReader reader)
        {
            while (reader.Read())
            {

                listData.DownUrl = (reader.GetSchemaTable().Columns["DownUrl"] != null) ? Convert.ToString(reader["DownUrl"]) : null;
                //没有这一列时,让其等于null
                list.Add(listData);
            }
            reader.Close();
        }

1

および/または代入で三項演算子を使用する:

employee.FirstName = rdr.IsDBNull(indexFirstName))? 
                     String.Empty: rdr.GetString(indexFirstName);

各プロパティタイプに応じて、デフォルト(nullの場合)の値を置き換えます...


1

このメソッドは、0から始まる列の序数であるindexFirstNameに依存しています。

if(!sqlReader.IsDBNull(indexFirstName))
{
  employee.FirstName = sqlreader.GetString(indexFirstName);
}

列のインデックスがわからないが名前を確認したくない場合は、代わりにこの拡張メソッドを使用できます。

public static class DataRecordExtensions
{
    public static bool HasColumn(this IDataRecord dr, string columnName)
    {
        for (int i=0; i < dr.FieldCount; i++)
        {
            if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                return true;
        }
        return false;
    }
}

そして、このようなメソッドを使用します:

if(sqlReader.HasColumn("FirstName"))
{
  employee.FirstName = sqlreader["FirstName"];
}

1

古い質問ですが、おそらく誰かが答えを必要としています

実際に私はこの問題をそのように回避しました

intの場合:

public static object GatDataInt(string Query, string Column)
    {
        SqlConnection DBConn = new SqlConnection(ConnectionString);
        if (DBConn.State == ConnectionState.Closed)
            DBConn.Open();
        SqlCommand CMD = new SqlCommand(Query, DBConn);
        SqlDataReader RDR = CMD.ExecuteReader();
        if (RDR.Read())
        {
            var Result = RDR[Column];
            RDR.Close();
            DBConn.Close();
            return Result;
        }
        return 0;
    }

""は空の文字列であるため、文字列の場合も0ではなく ""を返します。

だからあなたはそれを好きに使うことができます

int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;

そして

string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;

非常に柔軟なので、任意のクエリを挿入して任意の列を読み取ることができ、エラーで戻ることはありません


0

@marc_sの回答に基づいて、必要に応じて他のユーザーが使用できるヘルパークラスを次に示します。

public static class SQLDataReaderExtensions
    {
        public static int SafeGetInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? 0 : dataReader.GetInt32(fieldIndex);
        }

        public static int? SafeGetNullableInt(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as int?;
        }

        public static string SafeGetString(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? string.Empty : dataReader.GetString(fieldIndex);
        }

        public static DateTime? SafeGetNullableDateTime(this SqlDataReader dataReader, string fieldName)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.GetValue(fieldIndex) as DateTime?;
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName)
        {
            return SafeGetBoolean(dataReader, fieldName, false);
        }

        public static bool SafeGetBoolean(this SqlDataReader dataReader, string fieldName, bool defaultValue)
        {
            int fieldIndex = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldIndex) ? defaultValue : dataReader.GetBoolean(fieldIndex);
        }
    }

0

ConvertはDbNullを適切に処理します。

employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));

DBNullはnull値ではなく空の文字列に変換されることに注意してください。
Rhys Jones

-2

これも確認できます

if(null !=x && x.HasRows)
{ ....}

-1これは重要ではありません。NULL値または空の値ではなく、NULL列値のケースを処理していますSqlDataReader
青みがかった
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.