web-dev-qa-db-fra.com

Types C # DBNull et nullable - forme de conversion la plus propre

J'ai un DataTable, qui a un certain nombre de colonnes. Certaines de ces colonnes peuvent être annulées.

DataTable dt;  // Value set. 
DataRow dr;  // Value set. 

// dr["A"] is populated from T-SQL column defined as: int NULL 

Quelle est donc la forme la plus propre de conversion d'une valeur dans un DataRow en une variable nullable.

Idéalement, je serais capable de faire quelque chose comme:

int? a = dr["A"] as int?; 

Edit : Il s'avère que vous POUVEZ le faire, l'effet secondaire étant que si votre Les types de schéma ne sont pas des entiers, alors cela retournera TOUJOURS null. La réponse de Ruben d'utiliser dr.Field<int?>("A") garantit que les incompatibilités de types n'échouent pas en silence. Ceci, bien sûr, sera détecté par des tests unitaires approfondis.

Au lieu de cela, je tape habituellement quelque chose comme:

int? a = dr["A"] != DBNull.Value ? (int)dr["A"] : 0; 

C'est un tas de touches supplémentaires, mais plus important encore, il y a plus de place pour que quelqu'un bourre quelque chose avec une mauvaise frappe. Oui, un test unitaire le ramènera, mais je préfère l'arrêter complètement.

Quel est le modèle le plus propre et le moins sujet aux erreurs pour cette situation.

37
user111013

Le chapitre LINQ to DataSets de LINQ in Action est une bonne lecture.

Une chose que vous verrez est le Field<T> méthode d'extension, qui est utilisée comme suit: -

int? x = dr.Field<int?>( "Field" );

Ou

int y = dr.Field<int?>( "Field" ) ?? 0;

Ou

var z = dr.Field<int?>( "Field" );
55
Ruben Bartelink

C'est le but de la classe DataRowExtensions dans .NET 3.5, qui fournit des Field<T> et SetField<T> méthodes pour aller-retour des données nullables (et non nullables) entre les types DataRow et .NET.

int? fld = row.Field<int?>("ColumnA")

définira fld sur null si row["ColumnA"] contient DBNull.Value, à sa valeur s'il contient un entier, et lève une exception s'il contient autre chose. Et sur le chemin du retour,

row.SetField("ColumnA", fld);

fait la même chose en sens inverse: si fld contient null, il définit row["ColumnA"] à DBNull.Value, et lui attribue la valeur fld.

Il existe des surcharges de Field et SetField pour tous les types de valeurs pris en charge par DataRow (y compris les types non nullables), vous pouvez donc utiliser le même mécanisme pour obtenir et définir quels que soient leur type de données.

12
Robert Rossney
int? a = (int?)dr["A"]
4
bniwredyc

Les éléments suivants fonctionneraient en toute sécurité:

Snip:

public static class SqlDataReaderEx
{
    public static int TryParse(SqlDataReader drReader, string strColumn, int nDefault)
    {
        int nOrdinal = drReader.GetOrdinal(strColumn);
        if (!drReader.IsDbNull(nOrdinal))
            return drReader.GetInt32(nOrdinal);
        else
            return nDefault;
    }
}

tilisation:

SqlDataReaderEx.TryParse(drReader, "MyColumnName", -1);
1
KMån

Pourquoi ne pas utiliser LINQ? Il fait la conversion pour vous.

1
stepanian

Méthodes d'extension!

Quelque chose comme ceci:

public static class DataRowExtensions
{
    public static Nullable<T> GetNullableValue<T>(this DataRow row, string columnName)
        where T : struct
    {
        object value = row[columnName];
        if (Convert.IsDBNull(value))
            return null;

        return (Nullable<T>)value;
    }

    public static T GetValue<T>(this DataRow row, string columnName)
        where T : class
    {
        object value = row[columnName];
        if (Convert.IsDBNull(value))
            return null;

        return (T)value;
    }
}

Utilisez-le comme ceci:

int? a = dr.GetNullableValue<int>("A");

ou

string b = dr.GetValue<string>("B");
0
Brannon