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 :

SQLite SQLiteDataAdapter et UpdateCommand


Sujet :

C#

  1. #1
    Membre Expert

    Homme Profil pro
    Retraité
    Inscrit en
    Novembre 2007
    Messages
    3 587
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Novembre 2007
    Messages : 3 587
    Par défaut SQLite SQLiteDataAdapter et UpdateCommand
    Bonjour

    Encore besoin d'aide ...

    Je n'arrive pas à comprendre le fonctionnement du SQLiteDataAdapter.

    Voulant afficher dans DatagridView le résultat d'une requête avec un JOIN, je me suis trouvé" face à une erreur au moment de valider les données dans la base.

    System.Data.DBConcurrencyException*: 'Concurrency violation: the UpdateCommand affected 0 of the expected 1 records.'
    En cherchant sur le net, j'ai compris (peut-être à tort) que UpdateCommand ne peut pas faire la mise à jour à partir de ce genre de requête si j'utilise le SQLiteCommandBuilder.

    J'ai donc décidé de faire ma propre requête d'update pour ne mettre à jour QUE la table principale utilisée dans la requête.
    Mais j'obtiens toujours la même erreur alors que cette requête ne concerne qu'une seule table.

    Voilà mon code :

    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
                    adapter = new SQLiteDataAdapter();
     
                    // Requête qui ne pose pas de problème
                    //adapter.SelectCommand = new SQLiteCommand("select d.id, d.name, d.description from documents d", cnx);
     
                    // Requête qui déclenche l'erreur au moment de l'update.
                    adapter.SelectCommand = new SQLiteCommand("select g.name Groupe, d.name `Nom document`, d.description" +
                        " from documents d inner join groups g on d.group_id = g.id", cnx); 
     
                    // Requête en UPDATE qui ne concerne que la table principale
                    adapter.UpdateCommand = new SQLiteCommand("update documents set name = @name, description = @description " +
                        "where id = @id", cnx);
                    adapter.UpdateCommand.Parameters.Add("@name", DbType.String, 100, "name");
                    adapter.UpdateCommand.Parameters.Add("@description", DbType.String, 100, "description");                
                    var idParam = adapter.UpdateCommand.Parameters.Add("@id", DbType.Int32, 0, "id");
                    idParam.SourceVersion = DataRowVersion.Original; 
     
                    table = new DataTable();
     
                    adapter.Fill(table);
                    _bindingSource.DataSource = table;
    Pourquoi cette requête déclenche l'erreur alors que je ne traite qu'une seule table ? :-(
    J-L aka Papy pour les amis

  2. #2
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 942
    Par défaut
    Hello,

    L'erreur DBConcurrencyException se produit car votre objet DataAdapter tente de mettre à jour la ligne en base de données et réalise qu'il a affecté 0 ligne.

    Pourquoi 0 ligne ? Parce que votre requête SELECT initiale ne ramène pas la colonne id. Par conséquent, quand votre objet DataTable est rempli, la colonne id n'existe pas en mémoire.

    Lors de l'appel à la méthode adapter.Update(), le paramètre @id cherche sa colonne source associée (qui s'appelle "id"), ne la trouve pas (ou bien il lui associe la valeur NULL), et la requête finale envoyée au moteur SQLite ressemble alors à cela :
    UPDATE documents SET ... WHERE id = NULL.

    Pour corriger ce problème de façon définitive, il vous suffit de rajouter explicitement d.id au début de votre SelectCommand afin que votre objet DataTable dispose bien de la clé primaire nécessaire à la mise à jour :

    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    adapter.SelectCommand = new SQLiteCommand("select d.id, g.name Groupe, d.name `Nom document`, d.description" +
        " from documents d inner join groups g on d.group_id = g.id", cnx);

    Note : votre UpdateCommand actuel est déjà correctement configuré pour capter cette colonne "id" via votre instruction adapter.UpdateCommand.Parameters.Add("@id", DbType.Int32, 0, "id");.

    En espérant que cela vous débloque !

    EDIT:

    Au passage, si vous me permettez quelques conseils d'architecture pour l'évolution de votre projet :

    1. Envisagez d'abandonner DataTable et DataAdapter
    Ces classes appartiennent aux anciennes générations d'ADO.NET. Leur principal défaut est qu'elles ne sont pas "fortement typées". Comme vous venez d'en faire l'expérience avec la colonne id manquante, si une erreur se glisse dans votre requête SQL, le compilateur C# n'y verra que du feu et l'application plantera à l'exécution.

    2. Privilégiez des objets C# fortement typés (Classes ou Records)
    Il est beaucoup plus robuste de mapper vos résultats SQL directement dans des objets C# plutôt que d'accéder à des cellules par leur nom de chaîne de caractères (qui est très fragile). Par exemple, avec les record modernes :
    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    public record Document(int Id, string Nom, string Description);

    3. Utilisez un Micro-ORM comme Dapper
    Pour faciliter ce mapping sans alourdir votre code, la communauté .NET utilise massivement Dapper (disponible sur NuGet). C'est une bibliothèque très légère et performante qui gère toute la plomberie ADO.NET pour vous.

    Votre code de mise à jour deviendrait aussi simple et sécurisé que ceci :
    Code csharp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    // Dapper exécute la requête et mappe automatiquement les propriétés de l'objet "doc" aux paramètres @
    string sql = "UPDATE documents SET name = @Nom, description = @Description WHERE id = @Id";
    cnx.Execute(sql, doc);

    4. Pensez aux blocs using
    Si ce n'est pas déjà le cas dans le reste de votre code, pensez bien à utiliser les instructions using (ou using var) pour vos instances de SQLiteConnection et SQLiteCommand. Cela garantit que les ressources natives (hors mémoire managée) de SQLite seront correctement libérées et empêchera des fuites de mémoire ou des bases de données verrouillées (verrou SQLite).

    Bon courage pour la suite de votre projet !
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  3. #3
    Membre Expert

    Homme Profil pro
    Retraité
    Inscrit en
    Novembre 2007
    Messages
    3 587
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Novembre 2007
    Messages : 3 587
    Par défaut
    Alors déjà , MERCIIIII

    Je n'avais pas compris que l'id était nécessaire dans le select. Vraiment ... trop fort !
    Ca fonctionne maintenant très bien.

    Au passage, si vous me permettez quelques conseils d'architecture pour l'évolution de votre projet :
    Bien évidemment que je suis preneur de tous les bons conseils ... Même s'il ne s'agit que d'un projet perso, j'essaie de le faire au mieux.

    J'imagine que vos conseils 1, 2 et 3 vont de pair.
    J'ai utilisé ces composants après avoir fait des recherches sur le net qui montraient leur utilisation.
    Il y a bien longtemps, pour la seule application C# que j'avais réalisée, j'avais utilisé NHibernate mais aujourd'hui je ne saurais même plus par où commencer .
    Je vais regardé l'utilisation de Dapper.

    Pour l'utilisation des blocs using, c'est vrai que je n'ai pas trop le réflexe pour le moment.
    Il faut que je me corrige sur ce point.


    Merci encore pour m'avoir permis d'avancer. Je reviendrai une fois que j'aurais étudié Dapper.
    J-L aka Papy pour les amis

  4. #4
    Membre Expert

    Homme Profil pro
    Retraité
    Inscrit en
    Novembre 2007
    Messages
    3 587
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 64
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Novembre 2007
    Messages : 3 587
    Par défaut
    Juste un complément à propos de Dapper.
    Dans les packages NuGet il y en a beaucoup et j'espère avoi bien choisi :

    • Dapper
    • Dapper.Contrib
    • Dapper.ParameterExtensions
    • Dapper.SQlBuilder


    Je ne sais pas si j'ai bien fait de prendre tout ça, j'ai choisi au "feeling" en fonction des desciptions de chacun.
    J-L aka Papy pour les amis

  5. #5
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 942
    Par défaut
    Gardez uniquement Dapper et éventuellement Dapper.Contrib.
    Désinstallez les autres. Moins vous avez de packages, plus votre projet sera propre, léger, et facile à mettre à jour !
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

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

Discussions similaires

  1. qui connait sqlite ?
    Par Emmanuel Lecoester dans le forum SQLite
    Réponses: 23
    Dernier message: 19/02/2010, 13h44
  2. [SQLite]éliminer un motif dans des champ TEXT
    Par Invité(e) dans le forum Langage SQL
    Réponses: 2
    Dernier message: 26/05/2005, 17h44
  3. base de données en sqlite
    Par Cyrillou dans le forum SQLite
    Réponses: 1
    Dernier message: 12/05/2005, 15h37
  4. debuter en SQLite
    Par venomelektro dans le forum SQLite
    Réponses: 4
    Dernier message: 08/12/2004, 19h17
  5. [C#] Problème avec UpdateCommand
    Par fabrice1596 dans le forum Windows Forms
    Réponses: 2
    Dernier message: 08/12/2004, 17h15

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