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?

241
demandé sur DenaliHardtail 2009-11-20 20:24:33

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.

375
répondu marc_s 2016-08-06 07:15:55

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.

204
répondu stevehipwell 2010-07-22 11:54:22

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;
26
répondu Gone Coding 2014-10-07 15:10:48

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]);
17
répondu ZXX 2014-09-18 07:37:26

Une façon de le faire est de vérifier les valeurs NULL db:

employee.FirstName = (sqlreader.IsDBNull(indexFirstName) 
    ? ""
    : sqlreader.GetString(indexFirstName));
11
répondu Michael Todd 2009-11-20 20:17:36

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();
10
répondu el bayames 2013-01-08 16:22:53

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);
}
9
répondu Summer-Time 2016-04-29 20:37:13

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 }
9
répondu PJ3 2016-07-28 08:20:18

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.

7
répondu alex 2011-07-22 08:13:55

Vérifier sqlreader.IsDBNull(indexFirstName) avant d'essayer de le lire.

6
répondu CesarGon 2009-11-20 17:28:26

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"]);
                         }
4
répondu Vijai 2016-09-20 00:13:34

Je pense que vous voudriez utiliser:

SqlReader.IsDBNull(indexFirstName)
3
répondu bytebender 2009-11-20 17:31:50

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;
3
répondu Usman Ali 2017-12-11 15:43:59

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).

2
répondu Sonny Boy 2009-11-20 17:27:39

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

2
répondu Ahmed M.Kamal 2018-02-11 22:11:22

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];
}
1
répondu Tequila 2012-11-02 13:43:28
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
répondu xux 2013-05-31 06:25:11

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é...

1
répondu Charles Bretana 2014-09-18 12:35:23

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"];
}
1
répondu Ogglas 2016-02-12 12:47:28

Vous pouvez utiliser l'opérateur conditionnel:

employee.FirstName = sqlreader["indexFirstName"] != DBNull.Value ? sqlreader[indexFirstName].ToString() : "";
1
répondu Panayot Minkov 2017-08-22 12:45:40

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);
    }
0
répondu getpsyched 2018-09-06 12:42:27

, Vous pouvez toujours vérifier

if(null !=x && x.HasRows)
{ ....}
-2
répondu scooby 2011-07-22 08:14:05