web-dev-qa-db-ja.com

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

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

employee.FirstName = sqlreader.GetString(indexFirstName);

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

262
DenaliHardtail

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の値について再度心配する必要はありません。

414
marc_s

デフォルト値には、as演算子を??演算子と組み合わせて使用​​する必要があります。値型はNULL可能として読み取り、デフォルトを指定する必要があります。

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

as演算子はDBNullのチェックを含むキャストを処理します。

217
stevehipwell

文字列の場合は、単純にオブジェクトのバージョン(配列演算子を使用してアクセス)をキャストし、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;
28
Gone Coding

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]);
19
ZXX

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

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

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

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

私は以下を使います、そしてあまり心配しないでください。

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

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

また、列名を扱う場合は、型を比較す​​るほうが快適な場合があります。

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

このソリューションはベンダー依存性が少なく、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);
}
9
Summer-Time

私がしがちなことは、SELECTステートメントのnull値を適切なものに置き換えることです。

SELECT ISNULL(firstname, '') FROM people

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

7
alex

読む前にsqlreader.IsDBNull(indexFirstName)をチェックしてください。

6
CesarGon

Nullをチェックし、NULLの場合はデフォルト値を含めるための汎用関数を作成できます。 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"]);
                         }
5
Vijai

私はあなたが使いたいと思うだろう:

SqlReader.IsDBNull(indexFirstName)
3
bytebender

Marc_sによる回答に加えて、SqlDataReaderから値を取得するためのより一般的な拡張メソッドを使用できます。

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

ヘルパーメソッドの作り方

文字列の場合

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;
3
Usman Ali

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

静的メソッドはnullをチェックするためのコードを含みます(このページの他の答えを見てください)。

2
Sonny Boy

条件演算子を使用することができます。

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

代入して三項演算子を使用します。

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

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

1
Charles Bretana
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
xux

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")
1
Project Mayhem

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

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

このメソッドは、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
Ogglas

古い質問ですが、まだ回答が必要な人がいるかもしれません。

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

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;
    }

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

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

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

そして

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

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

1
Ahmed M.Kamal

ここには役に立つ情報(そしていくつかの間違った情報)が広がっているので、たくさんの答えがあります。それをすべてまとめてみたいと思います。

この質問に対する簡単な答えは、DBNullをチェックすることです - ほとんどの人がこのビットに同意します:)

SQLデータ型ごとにNULL値を読み込むためにヘルパーメソッドを使用するのではなく、一般的なメソッドを使用すると、はるかに少ないコードでこれに対処できます。ただし、null許容値型と参照型の両方に対して単一のジェネリックメソッドを持つことはできません。これについては、 null許容型をジェネリックパラメーターとして使用できますか? および C#のジェネリック型制約はすべてNULL可能です

そのため、@ 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入力可能な列がない場合は、この問題を回避できます。データベースを管理している場合は、デフォルトで列はNULL以外になり、必要な場合にのみNULL可能になります。
  • キャストが間違っていると暗黙的にnullが返されるため、C#の 'as'演算子を使用してデータベース値をキャストしないでください。
  • デフォルト値式 を使用すると、int、datetime、bitなどの値の型に対してデータベースのnullがnull以外の値に変更されます。

最後に、私が発見したすべてのSQL Serverデータ型にわたって上記のメソッドをテストしている間は、SqlDataReaderからchar []を直接取得することはできません。

0
Rhys Jones

これは、@ marc_s answerに基づいて必要に応じて他の人が使用できるヘルパークラスです。

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
Raghav

変換DbNullを賢明に変換します。

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