1. #1
    Membre éclairé
    Avatar de bombseb
    Inscrit en
    juillet 2005
    Messages
    572
    Détails du profil
    Informations forums :
    Inscription : juillet 2005
    Messages : 572
    Points : 865
    Points
    865

    Par défaut Un DataReader associé à cette Command est déjà ouvert

    Bonjour,

    Dans mon appli, je boucle sur le résultat d'une requête, et en fonction de ces résultats je supprime d'autres lignes dans une autre table (voir le code ci dessous).
    Le problème est que j'ai ce message d'erreur à la ligne du cmd.Delete() :

    System.InvalidOperationException
    Un DataReader associé à cette Command est déjà ouvert. Il doit d'abord être fermé.
    Je pense que c'est un problème de Using ? Mais je ne vois pas trop comment faire...

    Est-ce que quelqu'un a une idée svp ?



    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
                    using (SqlConnection s = new SqlConnection(connectionString))
                    {
                        s.Open();
                        using (SqlCommand cmdSelect = new SqlCommand("SELECT nom_table FROM dbsync", s))
                        {
                            cmdSelect.CommandType = CommandType.Text;
                            using (SqlDataReader sqlDataReader = cmdSelect.ExecuteReader())
                            {
                                while (sqlDataReader.Read())
                                {
                                    string nom_table = sqlDataReader.GetString(0);
                                    this.Synchro(nom_table);
     
                                    using (SqlCommand cmdDelete = new SqlCommand($"DELETE FROM dbsync WHERE nom_table = '{nom_table}'", s))
                                    {
                                        cmdDelete.CommandType = CommandType.Text;
                                        cmdDelete.ExecuteNonQuery();
                                    }
                                }
                            }
                        }
                    }

  2. #2
    Membre expert Avatar de jopopmk
    Homme Profil pro
    Développeur informatique
    Inscrit en
    mars 2011
    Messages
    1 829
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

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

    Informations forums :
    Inscription : mars 2011
    Messages : 1 829
    Points : 3 426
    Points
    3 426

    Par défaut

    Salut,

    plusieurs questions :
    - sur quelle ligne intervient l'exception ?
    - qu'y a-t-il derrière ta méthode Synchro ?
    - si tu fais sauter ta seconde Command (cmdDelete) as-tu toujours la même exception ?

    NB : le using est justement là pour faire proprement les choses
    Plus je connais de langages, plus j'aime le C.

  3. #3
    Expert éminent sénior Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    avril 2007
    Messages
    12 869
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : avril 2007
    Messages : 12 869
    Points : 23 194
    Points
    23 194

    Par défaut

    il veut dire sur la connexion que tu as déjà un reader sur la connexion

    en effet le reader est un objet qui demande l'exécution et val lire les lignes au fur et à mesure (à chaque tour)
    donc tu ne peux pas utiliser la même connexion pour exécuter autre chose

    tu pourrais t'en sortir en utilisation une autre connexion simultanément

    mais d'une manière générale (sauf rare exception) quand on fait une boucle pour lire des données, puis que pour chaque on fait une requete c'est qu'on a raté un truc
    car une base de données c'est ensembliste

    genre ça
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    DELETE FROM dbsync WHERE nom_table IN (SELECT nom_table FROM dbsync)

    avec des requetes ensemblistes tu va pouvoir faire la même chose en 1000x plus rapide et avec moins de code


    en plus là tu créé un nouveau command à chaque tour de boucle
    même avec peu de bases on imagine facilement qu'en le créant une seule fois avant la boucle et en utilisant le même à chaque fois c'est mieux (mémoire utilisée etc...)

    ajouté à cela que si pour faire 2 requetes il te faut 20 lignes de code c'est pas top, on créé souvent une classe d'encapsulation d'accès aux données

    donc faut reprendre quelques cours je pense ^^
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  4. #4
    Rédacteur/Modérateur

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

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

    Informations forums :
    Inscription : juillet 2016
    Messages : 1 456
    Points : 4 874
    Points
    4 874
    Billets dans le blog
    5

    Par défaut

    Bonjour,

    Citation Envoyé par Pol63 Voir le message
    donc faut reprendre quelques cours je pense ^^
    Citation Envoyé par Pol63 Voir le message
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    DELETE FROM dbsync WHERE nom_table IN (SELECT nom_table FROM dbsync)
    On voit souvent la paille qui est dans l'oeil de son voisin, mais pas la poutre qui est dans le sien !
    Le code SQL que tu proposes peut aisément se simplifier

    Bon, en réalité, il y a une subtile différence, c'est dans le cas où nom_table serait NULL.

    Pour en revenir au problème initial, de ce que je comprends, la table dbsync est une table qui contient une liste d'enregistrements à traiter. Une fois traité, il faut les supprimer. Cela permet de gérer une liste d'attente.

    Le problème ici est que l'on récupère tous les enregistrements pour ensuite les supprimer un à un. Autrement dit, le résultat de la première requête va être influencé par le DELETE de la seconde requête. Du coup, pour s'en tirer il faut :
    • ou bien lire tous les enregistrements d'un coup, et ensuite les traiter un à un (il ne peut y avoir qu'un seul SqlReader par connexion, et non pas par SqlCommand comme le mentionne l'erreur);
    • ou bien utiliser une seconde connexion (mais il faudra en plus gérer le type de transaction à utiliser).


    Pour ma part, vu la situation, je simplifierai ainsi :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
     
    using (SqlConnection s = new SqlConnection(connectionString))
    using (SqlCommand cmd = new SqlCommand())
    {
       bool ok = true;
       string nom_table;
       s.Open();
       cmdSelect.Connection = s;
     
       while(ok)
       {
          cmd.CommandText = "SELECT TOP(1) nom_table FROM dbsync";
          cmd.Parameters.Clear();
          nom_table = cmd.ExecuteScalar() as string;
     
          if (!String.IsNullOrEmpty(nom_table))
          {
             this.Synchro(nom_table);
             cmd.CommandText = "DELETE FROM dbsync WHERE nom_table = @nomtable";
             cmd.Parameters.AddWithValue("@nomtable", nom_table);
             cmd.ExecuteNonQuery();
          }
          else 
          {
             ok = false;
          }
       }
    Ainsi, plus besoin de reader !

    A noter : le SELECT auquel j'ai rajouté la clause TOP(1), afin de dire que l'on ne prend qu'un seul enregistrement au lieu de tous.
    Du coup, il est possible, dans ce cas particulier, d'utiliser ExecuteScalar.
    Tant qu'il y a des enregistrements, on boucle.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

  5. #5
    Membre éclairé
    Avatar de bombseb
    Inscrit en
    juillet 2005
    Messages
    572
    Détails du profil
    Informations forums :
    Inscription : juillet 2005
    Messages : 572
    Points : 865
    Points
    865

    Par défaut

    Salut,

    Merci à vous de m'avoir répondu...

    Le problème ici est que l'on récupère tous les enregistrements pour ensuite les supprimer un à un. Autrement dit, le résultat de la première requête va être influencé par le DELETE de la seconde requête. Du coup, pour s'en tirer il faut :

    ou bien lire tous les enregistrements d'un coup, et ensuite les traiter un à un (il ne peut y avoir qu'un seul SqlReader par connexion, et non pas par SqlCommand comme le mentionne l'erreur);
    ou bien utiliser une seconde connexion (mais il faudra en plus gérer le type de transaction à utiliser).
    Je suis arrivé aux mêmes conclusions. J'ai fini par faire ça en deux étapes :
    - Traiter les lignes de mon select
    - Puis les supprimer

    C'est vrai que ca n'a pas trop de sens de supprimer les lignes d'une table sur laquelle on est en train de travailler...

    Petite question subsidiaire : Je suis allé voir dans la MSDN mais j'ai pas trop compris à quoi servait le ExecuteScalar...

  6. #6
    Rédacteur/Modérateur

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

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

    Informations forums :
    Inscription : juillet 2016
    Messages : 1 456
    Points : 4 874
    Points
    4 874
    Billets dans le blog
    5

    Par défaut

    Citation Envoyé par bombseb Voir le message
    Petite question subsidiaire : Je suis allé voir dans la MSDN mais j'ai pas trop compris à quoi servait le ExecuteScalar...
    Un SqlReader permet d'accéder à toutes les colonnes de tous les enregistrements.
    Le ExecuteScalar permet d'accéder à la première colonne du premier enregistrement.
    François DORIN
    Consultant informatique : conception, modélisation, développement (C#/.Net et SQL Server)
    Site internet | Profils Viadéo & LinkedIn
    ---------
    Page de cours : fdorin.developpez.com
    ---------
    N'oubliez pas de consulter la FAQ C# ainsi que les cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 11
    Dernier message: 30/11/2011, 13h42
  2. Réponses: 2
    Dernier message: 14/04/2010, 17h34
  3. Réponses: 2
    Dernier message: 07/11/2009, 12h56
  4. Réponses: 3
    Dernier message: 27/02/2007, 15h04
  5. Quelle est cette commande qui plante linux?
    Par 123quatre dans le forum Shell et commandes GNU
    Réponses: 11
    Dernier message: 18/02/2006, 14h48

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