私はデータベースからPOCOを構築するためにSQLdatareaderを使っています。データベース内でNULL値が検出された場合を除き、コードは機能します。たとえば、データベースのFirstName列にNULL値が含まれていると、例外がスローされます。
employee.FirstName = sqlreader.GetString(indexFirstName);
この状況でNULL値を処理するための最良の方法は何ですか?
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
の値について再度心配する必要はありません。
デフォルト値には、as
演算子を??
演算子と組み合わせて使用する必要があります。値型はNULL可能として読み取り、デフォルトを指定する必要があります。
employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);
as
演算子はDBNullのチェックを含むキャストを処理します。
文字列の場合は、単純にオブジェクトのバージョン(配列演算子を使用してアクセス)をキャストし、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;
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]);
これを行う1つの方法は、db nullをチェックすることです。
employee.FirstName = (sqlreader.IsDBNull(indexFirstName)
? ""
: sqlreader.GetString(indexFirstName));
列名を使用してデータリーダー内で行が返される場合、NULL列値はないと思います。
datareader["columnName"].ToString();
を実行すると、空の文字列になる可能性のある値が常に得られます(比較する必要がある場合はString.Empty
)。
私は以下を使います、そしてあまり心配しないでください。
employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
reader.IsDbNull(ColumnIndex)
は多くの答えが言うように機能します。
また、列名を扱う場合は、型を比較するほうが快適な場合があります。
if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
このソリューションはベンダー依存性が少なく、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);
}
私がしがちなことは、SELECTステートメントのnull値を適切なものに置き換えることです。
SELECT ISNULL(firstname, '') FROM people
ここで、すべてのnullを空白の文字列に置き換えます。その場合、あなたのコードはエラーになりません。
読む前にsqlreader.IsDBNull(indexFirstName)
をチェックしてください。
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"]);
}
私はあなたが使いたいと思うだろう:
SqlReader.IsDBNull(indexFirstName)
Marc_sによる回答に加えて、SqlDataReaderから値を取得するためのより一般的な拡張メソッドを使用できます。
public static T SafeGet<T>(this SqlDataReader reader, int col)
{
return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
}
ヘルパーメソッドの作り方
文字列の場合
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;
一連の静的メソッドを使用して、データリーダーからすべての値を引き出します。ですから、この場合はDBUtils.GetString(sqlreader(indexFirstName))
を呼び出します。静的/共有メソッドを作成する利点は、同じチェックを何度も繰り返す必要がないことです。
静的メソッドはnullをチェックするためのコードを含みます(このページの他の答えを見てください)。
条件演算子を使用することができます。
employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
代入して三項演算子を使用します。
employee.FirstName = rdr.IsDBNull(indexFirstName))?
String.Empty: rdr.GetString(indexFirstName);
各プロパティタイプに応じて、デフォルト値(nullの場合)を置き換えます。
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();
}
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")
データテーブルに読み込まれるExcelシートのnullセルを処理するために、以下にリストされたコードを使用しています。
if (!reader.IsDBNull(2))
{
row["Oracle"] = (string)reader[2];
}
このメソッドは、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"];
}
古い質問ですが、まだ回答が必要な人がいるかもしれません。
実際に私はこのような問題を回避しました
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;
非常に柔軟なので、任意のクエリを挿入して任意の列を読み取ることができ、エラーで返されることはありません。
ここには役に立つ情報(そしていくつかの間違った情報)が広がっているので、たくさんの答えがあります。それをすべてまとめてみたいと思います。
この質問に対する簡単な答えは、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可能であることを期待しているかどうかを判断できます。これは、昔に書かれたコードを見るときに非常に役立ちます。
ヒント
最後に、私が発見したすべてのSQL Serverデータ型にわたって上記のメソッドをテストしている間は、SqlDataReaderからchar []を直接取得することはできません。
これは、@ 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);
}
}
変換DbNullを賢明に変換します。
employee.FirstName = Convert.ToString(sqlreader.GetValue(indexFirstName));