IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

SQL Server datetime2 nullable et .NET


Sujet :

C#

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut SQL Server datetime2 nullable et .NET
    Bonjour,

    J'ai l'impression d'être tombé sur un trou dans la raquette, et j'aimerais votre avis sur la méthode la plus propre pour contourner le problème.

    Dans ma base de données, j'ai une table contenant une colonne du type datetime2.
    Cette colonne accepte les NULL.

    Dans mon programme, je souhaite charger cette donnée dans une variable de type DateTime.

    Avec mon SqlDataReader, si je fais dr.GetDatetime(x) alors tout fonctionne bien jusqu'à ce que j'aie une valeur nulle.
    A ce moment la méthode GetDatetime plante sauvagement.

    Avec le type SQL datetime tout court (sans le 2) je pouvais faire :
    dr.GetSqlDatetime(x) et tester si le résultat avait la proproété Value égale = DBNull.Value

    Seulement, dr.GetSqlDatetime(x) plante lorsque je tente de charger une colonne du type datetime2

    Il n'y a évidement pas de méthode GetSqlDatetime2, ça aurait été trop beau…

    Du coup, j'ai fait cette méthode d'extension, mais je trouve ça relativement crade...

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public static DateTime? GetNullableDateTime(this SqlDataReader dr, int column)
        {
            if (dr.IsDBNull(column))
            {
                return null;
            }
            else
            {
                return dr.GetDateTime(column);
            }
        }

    Notamment, ceci ne fonctionnera pas si mon SqlCommand a pour optimisation "forwardonly".

    Habituellement je gère les NULL de cette manière, que je trouve plus élégante :
    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
        public static int? OrNull(this SqlInt32 value)
        {
            return value.IsNull ? (int?)null : value.Value;
        }

  2. #2
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    Bonjour,
    Du coup, j'ai fait cette méthode d'extension, mais je trouve ça relativement crade...

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    public static DateTime? GetNullableDateTime(this SqlDataReader dr, int column)
        {
            if (dr.IsDBNull(column))
            {
                return null;
            }
            else
            {
                return dr.GetDateTime(column);
            }
        }
    Ce n'est pas crade. C'est même ainsi qu'il faut faire.

    De mon côté, je n'ai pas de méthode d'extension, je fais à la mano en utilisant l'opérateur ternaire : value = dr.IsDBNull(column) ? (DateTime?)null : dr.GetDateTime(column)
    Citation Envoyé par StringBuilder Voir le message
    Notamment, ceci ne fonctionnera pas si mon SqlCommand a pour optimisation "forwardonly".
    C'est-à-dire ? J'avoue ne pas comprendre. Un SqlDataReader obtenu via un SqlCommand est par nature forwardonly. Et ce code est tout à fait fonctionnel dans le cas présent. Le forward only ne signifie pas qu'on ne peut pas appeler plusieurs méthodes sur la même colonne, il indique seulement qu'on ne peut pas revenir à une ligne précédente.

  3. #3
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    Si c'est pas forward only, en tout cas j'ai déjà eu le souci par le passé avec une option de lecture qui interdisait :
    - De lire les colonnes dans le désordre (forcément de manière incrémentielle)
    - De lire plusieurs fois la même colonne

    Du coup j'avais été obligé de faire des GetValues() à chaque ligne et me démerder avec un object[], ce qui ne me plaisait pas du tout.

    Après, les méthodes d'extension je trouve ça beaucoup plus facile à écrire et à relire :

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    value = dr.GetNullableDateTime(column);

    Seul truc, c'est que ça fait tâche par rapport aux autres types que je gère de cette manière :

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    value = dr.GetSqlInt32(column).OrNull();

    L'avantage c'est que si la colonne n'est pas nullable dans la base, j'ai juste à supprimer le "OrNull()", et si je veux que ma variable prenne une valeur par défaut, je peux faire un "OrDefault(default)" sur le même modèle

  4. #4
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    Si c'est pas forward only, en tout cas j'ai déjà eu le souci par le passé avec une option de lecture qui interdisait :
    - De lire les colonnes dans le désordre (forcément de manière incrémentielle)
    - De lire plusieurs fois la même colonne

    Du coup j'avais été obligé de faire des GetValues() à chaque ligne et me démerder avec un object[], ce qui ne me plaisait pas du tout.
    Je peux tout à fait comprendre la gêne, mais je n'ai jamais rencontré ce soucis avec SqlDataReader. Peut être était-ce avec un autre fournisseur ?

    Citation Envoyé par StringBuilder Voir le message
    Après, les méthodes d'extension je trouve ça beaucoup plus facile à écrire et à relire :

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    value = dr.GetNullableDateTime(column);

    Seul truc, c'est que ça fait tâche par rapport aux autres types que je gère de cette manière :

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    value = dr.GetSqlInt32(column).OrNull();

    L'avantage c'est que si la colonne n'est pas nullable dans la base, j'ai juste à supprimer le "OrNull()", et si je veux que ma variable prenne une valeur par défaut, je peux faire un "OrDefault(default)" sur le même modèle
    Rien ne t'empêche de faire une méthode d'extension qui permet de retourner un SqlDateTime à partir d'une colonne DATETIME2. Par contre, il faut avoir conscience qu'il peut y avoir de légère perte. SqlDateTime est fait pour stocker un DATETIME et non un DATETIME2, il doit certainement souffrir des mêmes limitations. Mais ainsi, tu pourrais avoir quelque chose d'homogène, avoir une méthode nommée GetSqlDateTime2.

    Après, si les limitations sont un soucis, tu peux aussi aller plus loin et créer une classe SqlDateTime2.

    Avec tout ça, tu pourrais tout gérer de la même manière.

    J'en profite pour donner un lien utile, regroupant l'ensemble des mappings entre les types SQL Server et les types .NET : https://docs.microsoft.com/fr-fr/dot...-type-mappings

  5. #5
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    A l'inverse, le datetime2 de SQL Server a été créé afin de répondre convenablement à la norme SQL, elle-même basée sur ISO 8601... sur laquelle est basée elle-même le C#.

    Ainsi, le DateTime de C# et le datetime2 de SQL Server ont la même précision et le même time range.
    J'imagine qu'ils ont aussi la même représentation interne, ce qui maximise potentiellement les performances, là où le datetime de SQL Server oblige à effectuer une conversion.

    DateTime C# :
    https://msdn.microsoft.com/fr-fr/lib...v=vs.110).aspx
    Time values are measured in 100-nanosecond units called ticks, and a particular date is the number of ticks since 12:00 midnight, January 1, 0001 A.D. (C.E.) in the GregorianCalendar calendar (excluding ticks that would be added by leap seconds).
    DateTime SQL Server :
    https://docs.microsoft.com/en-us/sql...ql-server-2017
    datetime is not ANSI or ISO 8601 compliant.

    YYYY is four digits from 1753 through 9999 that represent a year.

    MM is two digits, ranging from 01 to 12, that represent a month in the specified year.

    DD is two digits, ranging from 01 to 31 depending on the month, that represent a day of the specified month.

    hh is two digits, ranging from 00 to 23, that represent the hour.

    mm is two digits, ranging from 00 to 59, that represent the minute.

    ss is two digits, ranging from 00 to 59, that represent the second.

    n* is zero to three digits, ranging from 0 to 999, that represent the fractional seconds.

    Accuracy
    Rounded to increments of .000, .003, or .007 seconds
    datetime2 SQL Server :
    The ANSI and ISO 8601 compliance of date and time apply to datetime2.

    YYYY is a four-digit number, ranging from 0001 through 9999, that represents a year.

    MM is a two-digit number, ranging from 01 to 12, that represents a month in the specified year.

    DD is a two-digit number, ranging from 01 to 31 depending on the month, that represents a day of the specified month.

    hh is a two-digit number, ranging from 00 to 23, that represents the hour.

    mm is a two-digit number, ranging from 00 to 59, that represents the minute.

    ss is a two-digit number, ranging from 00 to 59, that represents the second.

    n* is a zero- to seven-digit number from 0 to 9999999 that represents the fractional seconds. In Informatica, the fractional seconds will be truncated when n > 3.

    Accuracy
    100 nanoseconds

  6. #6
    Expert confirmé

    Avatar de François DORIN
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Juillet 2016
    Messages
    2 761
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2016
    Messages : 2 761
    Billets dans le blog
    21
    Par défaut
    Citation Envoyé par StringBuilder Voir le message
    A l'inverse, le datetime2 de SQL Server a été créé afin de répondre convenablement à la norme SQL, elle-même basée sur ISO 8601... sur laquelle est basée elle-même le C#.
    Tout à fait. Le plus gros problème était surtout la date minimale (1753) pour DATETIME au lieu de 0001 comme le prévoit la norme.

    Citation Envoyé par StringBuilder Voir le message
    J'imagine qu'ils ont aussi la même représentation interne, ce qui maximise potentiellement les performances, là où le datetime de SQL Server oblige à effectuer une conversion.
    Ce n'est pas sûr. Le type DATETIME2 a une longueur variable alors que la structure DateTime en C# est fixe. Je doute donc que la représentation interne soit la même.

Discussions similaires

  1. Accès à SQL Server à partir de ASP.Net
    Par Abdou_moujar dans le forum MS SQL Server
    Réponses: 6
    Dernier message: 25/01/2008, 18h34
  2. Creation d'une base SQL Server 2005 avec VB.net 2005 Express
    Par Jeannot2 dans le forum Accès aux données
    Réponses: 4
    Dernier message: 20/08/2007, 20h54
  3. Problème connexion sql server 2005 avec asp.net en C#
    Par PKO06 dans le forum MS SQL Server
    Réponses: 8
    Dernier message: 29/05/2007, 18h46
  4. Connection à une base SQL Server 2005 avec ASP.NET en C#
    Par LaDeveloppeuse dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 24/05/2007, 10h29
  5. [SQL Server] SQL Server 2005 CTP & VS.net
    Par Pleymo dans le forum MS SQL Server
    Réponses: 6
    Dernier message: 22/11/2005, 18h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo