Lecteur de données SQL-Gestion des valeurs de colonne Null
J'utilise un SQLdatareader pour construire des poco à partir d'une base de données. Le code fonctionne, sauf lorsqu'il rencontre une valeur null dans la base de données. Par exemple, si la colonne FirstName de la base de données contient une valeur null, une exception est levée.
employee.FirstName = sqlreader.GetString(indexFirstName);
Quelle est la meilleure façon de gérer les valeurs null dans cette situation?
22 réponses
, Vous devez vérifier pour IsDBNull
:
if(!SqlReader.IsDBNull(indexFirstName))
{
employee.FirstName = sqlreader.GetString(indexFirstName);
}
C'est votre seul moyen fiable de détecter et de gérer cette situation.
J'ai enveloppé ces choses dans des méthodes d'extension et j'ai tendance à renvoyer une valeur par défaut si la colonne est en effet null
:
public static string SafeGetString(this SqlDataReader reader, int colIndex)
{
if(!reader.IsDBNull(colIndex))
return reader.GetString(colIndex);
return string.Empty;
}
Maintenant, vous pouvez l'appeler comme ceci:
employee.FirstName = SqlReader.SafeGetString(indexFirstName);
Et vous n'aurez plus jamais à vous soucier d'une exception ou d'une valeur null
.
Vous devez utiliser l'opérateur as
combiné avec l'opérateur ??
pour les valeurs par défaut. Les types de valeur devront être lus comme nullable et donnés par défaut.
employee.FirstName = sqlreader[indexFirstName] as string;
employee.Age = sqlreader[indexAge] as int? ?? default(int);
L'opérateur as
gère le casting, y compris la vérification de DBNull.
Pour une chaîne, vous pouvez simplement lancer la version de l'objet (accessible à l'aide de l'opérateur de tableau) et terminer avec une chaîne null pour les valeurs NULL:
employee.FirstName = (string)sqlreader[indexFirstName];
Ou
employee.FirstName = sqlreader[indexFirstName] as string;
Pour les entiers, si vous convertissez en entier nullable, vous pouvez utiliser GetValueOrDefault ()
employee.Age = (sqlreader[indexAge] as int?).GetValueOrDefault();
Ou l'opérateur de coalescence null (??
).
employee.Age = (sqlreader[indexAge] as int?) ?? 0;
IsDbNull(int)
est généralement beaucoup plus lent que d'utiliser des méthodes comme GetSqlDateTime
et ensuite de comparer à DBNull.Value
. Essayez ces méthodes d'extension pour 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;
}
Utilisez-les comme ceci:
var dd = r.Val<DateTime>(ords[4]);
var ii = r.Def<int>(ords[0]);
int nn = r.Def<int>(ords[0]);
Une façon de le faire est de vérifier les valeurs NULL db:
employee.FirstName = (sqlreader.IsDBNull(indexFirstName)
? ""
: sqlreader.GetString(indexFirstName));
Je ne pense pas qu'il y ait une valeur de colonne NULL, lorsque les lignes sont renvoyées dans un datareader en utilisant le nom de la colonne.
Si vous ne datareader["columnName"].ToString();
, il vous donnera toujours une valeur qui peut être une chaîne vide (String.Empty
si vous avez besoin de comparer).
J'utiliserais ce qui suit et ne m'inquiéterais pas trop:
employee.FirstName = sqlreader["columnNameForFirstName"].ToString();
Cette Solution est moins dépendante du fournisseur et fonctionne avec un lecteur SQL, OleDB et 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);
}
reader.IsDbNull(ColumnIndex)
fonctionne comme beaucoup de réponses dit.
Et je veux mentionner si vous travaillez avec des noms de colonnes, juste comparer les types peut être plus confortable.
if(reader["TeacherImage"].GetType() == typeof(DBNull)) { //logic }
Ce que j'ai tendance à faire est de remplacer les valeurs null dans L'instruction SELECT par quelque chose d'approprié.
SELECT ISNULL(firstname, '') FROM people
Ici, je remplace chaque null par une chaîne vide. Votre code ne lancera pas d'erreur dans ce cas.
Vérifier sqlreader.IsDBNull(indexFirstName)
avant d'essayer de le lire.
Vous pouvez écrire une fonction générique pour vérifier Null et inclure la valeur par défaut lorsqu'elle est NULL. Appelez ceci lors de la lecture de Datareader
public T CheckNull<T>(object obj)
{
return (obj == DBNull.Value ? default(T) : (T)obj);
}
Lors de la lecture du Datareader, utilisez
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"]);
}
Je pense que vous voudriez utiliser:
SqlReader.IsDBNull(indexFirstName)
Comment créer des méthodes d'assistance
Pour La Chaîne
private static string MyStringConverter(object o)
{
if (o == DBNull.Value || o == null)
return "";
return o.ToString();
}
Utilisation
MyStringConverter(read["indexStringValue"])
Pour Int
private static int MyIntonverter(object o)
{
if (o == DBNull.Value || o == null)
return 0;
return Convert.ToInt32(o);
}
Utilisation
MyIntonverter(read["indexIntValue"])
Pour La Date
private static DateTime? MyDateConverter(object o)
{
return (o == DBNull.Value || o == null) ? (DateTime?)null : Convert.ToDateTime(o);
}
Utilisation
MyDateConverter(read["indexDateValue"])
Note: pour DateTime déclarer varialbe comme
DateTime? variable;
Nous utilisons une série de méthodes statiques pour tirer toutes les valeurs de nos données les lecteurs. Donc, dans ce cas, nous appellerions DBUtils.GetString(sqlreader(indexFirstName))
L'avantage de créer des méthodes statiques/partagées est que vous n'avez pas à faire les mêmes vérifications encore et encore...
La ou les méthodes statiques contiendraient du code pour vérifier les valeurs NULL (voir autres réponses sur cette page).
Ancienne question mais peut-être que quelqu'un a encore besoin d'une réponse
En vrai, j'ai travaillé autour de ce problème comme ça
Pour 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;
}
La même chose pour la chaîne retourne simplement ""au lieu de 0 Car "" est une chaîne vide
Donc vous pouvez l'utiliser comme
int TotalPoints = GatDataInt(QueryToGetTotalPoints, TotalPointColumn) as int?;
Et
string Email = GatDatastring(QueryToGetEmail, EmailColumn) as string;
Très flexible pour que vous puissiez insérer n'importe quelle requête pour lire n'importe quelle colonne et elle ne reviendra jamais avec Erreur
J'utilise le code ci-dessous pour gérer les cellules nulles dans une feuille Excel qui est lue dans un datatable.
if (!reader.IsDBNull(2))
{
row["Oracle"] = (string)reader[2];
}
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();
}
Et / ou utiliser l'opérateur ternaire avec affectation:
employee.FirstName = rdr.IsDBNull(indexFirstName))?
String.Empty: rdr.GetString(indexFirstName);
Remplacez la valeur par défaut (lorsque null) appropriée pour chaque type de propriété...
Cette méthode dépend de indexFirstName qui devrait être l'ordinal de colonne basé sur zéro.
if(!sqlReader.IsDBNull(indexFirstName))
{
employee.FirstName = sqlreader.GetString(indexFirstName);
}
Si vous ne connaissez pas l'index des colonnes mais ne voulez pas vérifier un nom, vous pouvez utiliser cette méthode d'extension à la place:
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;
}
}
Et utilisez la méthode comme ceci:
if(sqlReader.HasColumn("FirstName"))
{
employee.FirstName = sqlreader["FirstName"];
}
Vous pouvez utiliser l'opérateur conditionnel:
employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
En complément de la réponse de marc_s, vous pouvez utiliser une méthode d'extension plus générique pour obtenir des valeurs de SqlDataReader:
public static T SafeGet<T>(this SqlDataReader reader, int col)
{
return reader.IsDBNull(col) ? default(T) : reader.GetFieldValue<T>(col);
}
, Vous pouvez toujours vérifier
if(null !=x && x.HasRows)
{ ....}