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

Bases de données Delphi Discussion :

Utiliser TClientDataSet en local


Sujet :

Bases de données Delphi

  1. #1
    Membre du Club
    Inscrit en
    Décembre 2002
    Messages
    74
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 74
    Points : 44
    Points
    44
    Par défaut Utiliser TClientDataSet en local
    Bonjour,

    Je charge des données dans un ClientDataSet via FDConnection, FDStoredProc, DataSetProvider sans problème
    Je veux maintenant pouvoir faire des modifications sur les données du ClientDataSet en local, c'est à dire sans remonter à la BD les modifications avec ClientDataSet.ProvideurName = ''. Et là au moment du post j'ai le message d'erreur "Tentative de modification d'un champ en lecture seule".

    Merci de dire comment paramétrer le ClientDataSet pour pouvoir fonctionner en local

    Delphi 10 Seattle, VCL, 32Bits

  2. #2
    Membre confirmé
    Avatar de alheuredudejeuner
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2005
    Messages
    376
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

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

    Informations forums :
    Inscription : Août 2005
    Messages : 376
    Points : 632
    Points
    632
    Billets dans le blog
    4
    Par défaut tant qu'à être avec Firedac,
    tant qu'à être avec Firedac,

    j'utiliserai un TFDMemTable à la place du TClientDataset, Pour les mises à jour serveur je fais un truc tordu avec des TFDDataMove.

    Cordialement

  3. #3
    Membre expert
    Avatar de Barbibulle
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    2 048
    Détails du profil
    Informations personnelles :
    Âge : 54
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 2 048
    Points : 3 342
    Points
    3 342
    Par défaut
    Bonjour,

    Avez vous bien paramétré le clientdataset pour qu'il utilise le cache des mises à jours ?

    LogChanges := true;

    Normalement avec LogChanges := true, ce n'est pas sur Post que les mises à jours sont appliqués sur le serveur mais lors de ApplyUpdates.

    Les "post" ne font que modifier le cache local.

    Votre erreur "Tentative de modification d'un champ en lecture seule" n'a à mon avis rien à voir. Faites un test de modification en étant connecté (donc sans mettre le ClientDataSet.ProvideurName = '') vous aurez à mon avis le même message. Avez vous tester avant de passer en "mode local" que les mises à jours fonctionnent correctement ?

  4. #4
    Membre du Club
    Inscrit en
    Décembre 2002
    Messages
    74
    Détails du profil
    Informations forums :
    Inscription : Décembre 2002
    Messages : 74
    Points : 44
    Points
    44
    Par défaut Utiliser TClientDataSet en local
    Bonjour,

    Merci de votre réponse,

    En fait je n'ai toujours pas résolu mon problème.

    Pour répondre à votre question
    <<Faites un test de modification en étant connecté (donc sans mettre le ClientDataSet.ProvideurName = '') vous aurez à mon avis le même message. Avez vous tester avant de passer en "mode local" que les mises à jours fonctionnent correctement ?>>

    En fait, je ne vais pas faire de mise à jour en mode connecté (et les mises à jour ne fonctionneraient pas). Une fois les données chargées dans le clientdataset, je veux pouvoir modifier son contenu (post) sans faire d'ApplyUpdates. Les modifications mémorisées dans le clientdataset seront traitées par une autre voie.

    C'est comme si le clientdataset mémorisait des contraintes dues au chargement de données qui provoqueraient cette erreur même si le lien est ensuite coupé (ClientDataSet.ProvideurName = '').

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Pour cloner les données d'un DataSet vers un TClientDataSet, j'utilise une fonction de mon crue lorsque je n'utilise pas la propriété Delta
    Le TClientDataSet est ainsi totalement en mémoire sans plus aucun lien avec sa source (voir la méthode TSLTClientDataSetAssistant.CloneDataSet)

    Comme toi, j'utilise mon propre système de mise à jour, j'ai une couche de persistance qui génère le SQL de mise à jour de la BD,
    je sais ainsi exactement ce qui est soumis au serveur (ce qui n'est pas du tout transparent avec ApplyUpdates ou les équivalents des bibliothèques tiers)

    A l'origine, j'ai créé cette couche de persistance lors d'un passage de BDE+Paradox en TTable vers un MySQL en mode pur SQL
    Au fil du temps, je l'ai fait évolué avec une gestion plus ou moins complète de MyDaC, ADO, DBX, ODAC ...

    Par contre, ton message n'est pas lié au Provider mais à un champ déclaré en ReadOnly dans son TField
    Il te faut peut-être modifié la structure des TField pour retirer le ReadOnly dans le TClientDataSet

    Pour ma part, j'ai fait la démarche inverse
    Je créé des TClientDataSet de toute pièce par des objets métier qui les encapsulent
    J'insère des données dedans
    Je verrouille certains champ en lecture seule pour obliger l'utilisation des méthodes métiers des objets encapsulant les DataSet


    Je n'ai plus le code où j'avais fait un clone de DataSet sans passer par un SetProvider mais cela est proche de cela
    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
    Src.Open();
    Src.First();
     
    Dest.FieldDefs.Assign(Src.FieldDefs);
    CreateDataSet();
    for iField := 0 to Dest.Fields.Count - 1 do
      Dest.Fields[iField].ReadOnly := False;
     
    while not Src.Eof do
    begin
      Dest.Append();
     
      for iField := 0 to Src.FieldCount - 1 do
        Dest.Fields[iField].AsVariant := Src.Fields[iField].AsVariant;
     
      Dest.Post();
      Src.Next();
    end;
    ou

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Src.Open();
    Dest := TSLTClientDataSetAssistant.CloneDataSet(Src);
     
    for iField := 0 to Dest.Fields.Count - 1 do
      Dest.Fields[iField].ReadOnly := False;

    Le Clone via SetProvider ne l'utilise que pour des ensembles de petites tailles
    Si la source est potentiellement volumineuse, j'utilise directement la source si celle-ci peut être modifié en locale sans impact sur le serveur (selon les libs sous-jacentes à mon TSLTDBQuery qui est une couche d'abstraction à la couche DB, c'est CachedUpdate, LocalUpdate, UpdateBatch ...)
    Cela permet de conserver une éventuelle gestion de pagination de paquets de données.
    A Dans un dernier cas, je ne vide pas le Provider et j'utilise PacketRecords !

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    //------------------------------------------------------------------------------
    (*                SoLuTions is an Versatile Library for Delphi                 -
     *                                                                             -
     *  Copyright ou © ou Copr. "SLT Solutions", (2006)                            -
     *  contributeur : ShaiLeTroll (2012) - Création du TSLTClientDataSetAssistant en C++Builder 2007
     *  contributeur : ShaiLeTroll (2015) - Traduction du TSLTClientDataSetAssistant en Delphi XE2
     *                                                                             -
     * Ce logiciel est un programme informatique servant à aider les développeurs  -
     * Delphi avec une bibliothèque polyvalente, adaptable et fragmentable.        -
     *                                                                             -
     * Ce logiciel est régi par la licence CeCILL-C soumise au droit français et   -
     * respectant les principes de diffusion des logiciels libres. Vous pouvez     -
     * utiliser, modifier et/ou redistribuer ce programme sous les conditions      -
     * de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA    -
     * sur le site "http://www.cecill.info".                                       -
     *                                                                             -
     * En contrepartie de l'accessibilité au code source et des droits de copie,   -
     * de modification et de redistribution accordés par cette licence, il n'est   -
     * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,   -
     * seule une responsabilité restreinte pèse sur l'auteur du programme,  le     -
     * titulaire des droits patrimoniaux et les concédants successifs.             -
     *                                                                             -
     * A cet égard  l'attention de l'utilisateur est attirée sur les risques       -
     * associés au chargement,  à l'utilisation,  à la modification et/ou au       -
     * développement et à la reproduction du logiciel par l'utilisateur étant      -
     * donné sa spécificité de logiciel libre, qui peut le rendre complexe à       -
     * manipuler et qui le réserve donc à des développeurs et des professionnels   -
     * avertis possédant  des  connaissances  informatiques approfondies.  Les     -
     * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du      -
     * logiciel à leurs besoins dans des conditions permettant d'assurer la        -
     * sécurité de leurs systèmes et ou de leurs données et, plus généralement,    -
     * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.          -
     *                                                                             -
     * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez      -
     * pris connaissance de la licence CeCILL-C, et que vous en avez accepté les   -
     * termes.                                                                     -
     *                                                                             -
     *----------------------------------------------------------------------------*)
     unit SLT.DB.DBClient;
     
    interface
     
    uses Data.DB, Datasnap.DBClient, MidasLib, Datasnap.Provider;
     
    type
      /// <summary>Boite à outil pour la classe TClientDataSet</summary>
      /// <remarks>Le TSLTClientDataSetAssistant n'est pas un class helper car cela n'était pas possible sous C++Builder 2007</remarks>
      TSLTClientDataSetAssistant = class(TObject)
        class function CloneDataSet(ADataSet: TDataSet): TDataSet;
        class function IsRecordCountable(ADataSet: TDataSet): Boolean;
      end;
     
    implementation
     
    //------------------------------------------------------------------------------
    class function TSLTClientDataSetAssistant.CloneDataSet(ADataSet: TDataSet): TDataSet;
    var
      ClientDS: TClientDataSet absolute Result;
      ProviderDS: TDataSetProvider;
    begin
      ClientDS := TClientDataSet.Create(nil);
     
      ProviderDS := TDataSetProvider.Create(nil);
      try
        // Je préfère SetProvider par rapport à ProviderName et sa bidouille avec le Owner !
        ClientDS.SetProvider(ProviderDS);
        try
          ClientDS.PacketRecords := -1; // garanti la copie des données !
          ProviderDS.DataSet := ADataSet;
          ClientDS.Open(); // Copie les Données d'un seul coup !
     
          // Par Défaut LogChanges est à true, ainsi les modifications restent en mémoire et cela ne tente pas d'altérer un DataSet qui encore lié malencontreusement sans un appel explicite à ApplyUpdates
          // Attribuez la valeur false à LogChanges si vous n'avez pas l'intention de mettre à jour la base de données avec les modifications de l'ensemble de données client
          // Avec le finally, il n'y a pas de risque d'un lien malencontreux
          ClientDS.LogChanges := False;
        finally
          ClientDS.SetProvider(nil); // on détache, on ne conserve que la copie en mémoire
        end;
      finally
        ProviderDS.Free();
      end;
    end;
     
    //------------------------------------------------------------------------------
    class function TSLTClientDataSetAssistant.IsRecordCountable(ADataSet: TDataSet): Boolean;
    begin
      Result := (ADataSet is TClientDataSet) and (TClientDataSet(ADataSet).PacketRecords = -1);
    end;
     
     
     
    end.



    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
    28
    29
    30
    31
     
    //------------------------------------------------------------------------------
    function TSLTPersistentORMDBSQLMarshallingEngine.CropDataSetIntoProxy(DataSetProxy: ISLTPersistentORMEntityDataSetProxy; AQuery: ISLTDBQuery): Boolean;
    var
      DirectMode: Boolean;
      DataSetInMemory: ISLTDBQueryDataSetMemory;
      DataSetExtractor: ISLTDBQueryDataSetExtractable;
    begin
      // Gestion de la mémoire (utilisation directe du DataSet ou passage par un clone)
      DirectMode := False;
      if not FUnMarshallingUseClonedDataSet then
      begin
        // Pour l'utilisation en directe : Il faut un DataSet qui se manipulent qu'en mémoire et dont l'instance peut être librement gérée !
        if Supports(AQuery, ISLTDBQueryDataSetMemory, DataSetInMemory) and Supports(AQuery, ISLTDBQueryDataSetExtractable, DataSetExtractor) then
        begin
          DataSetInMemory.OnlyMemory := True;
          DirectMode := True;
        end
      end;
      // Le DataSet interne au ISLTDBQuery passé en paramètre
      // sera libéré naturellement par compteur de référence, il faut donc
      // soit l'extraire pour prendre la responsabilité de la libération
      // soit le dupliquer complètement vers une autre instance
      if DirectMode then
        DataSetProxy.DataSet := DataSetExtractor.Extract()
      else
        DataSetProxy.DataSet := CloneDataSet(AQuery.DataSet);
     
      DataSetProxy.OwnsDataSet := True;
      Result := Assigned(DataSetProxy.DataSet);
    end;


    Au passage, un code où j'ai exploité le Delta,
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
     
    //------------------------------------------------------------------------------
    function Txxx.TAbstractMatrix.SaveMatrix(): Boolean;
    var
      DeltaCDS: TClientDataSet;
      ExpectedRowState: TUpdateStatus;
      Engine: ISaveEngine;
    begin
      Result := True;
     
      FSaving := True;
      try
        try
          DeltaCDS := TClientDataSet.Create(nil);
          try
            DeltaCDS.Data := Delta;
    //        DeltaCDS.SaveToFile(ClassName() + '.cds.xml', dfXMLUTF8);
     
            if not DeltaCDS.IsEmpty() then
            begin
              Engine := SaveEngineFactory();
              Engine.Delta := DeltaCDS;
     
              // Un groupe de delta est composé d'une ligne originale et d'une ligne avec les modifications
              // On commence par lire la ligne originale pour démarrer un groupe de delta
              ExpectedRowState := usUnmodified;
              DeltaCDS.First();
              while not DeltaCDS.EOF do
              begin
                if DeltaCDS.UpdateStatus = ExpectedRowState then
                begin
                  case ExpectedRowState of
                    // Valeurs originales d'un enregistrement modifié
                    usUnmodified:
                      begin
                        Engine.Original := DeltaCDS.Bookmark;
     
                        // Après avoir lu la ligne originale, on est en attente des modifications
                        ExpectedRowState := usModified;
                      end;
                    // Valeurs modifiées dans un enregistrement
                    usModified:
                      begin
                        Engine.Alteration := DeltaCDS.Bookmark;
                        if not Engine.Save() then
                          Exit(False);
     
                        // Après avoir lu une ligne de modification, le reprend le groupe depuis la début
                        ExpectedRowState := usUnmodified;
                      end;
                  end;
                end
                else
                  raise ExxxSaveError.CreateResFmt(@SErrIncoherentDeltaRowStateFmt, [ClassName()]);
     
                DeltaCDS.Next();
              end;
     
              // A la fin, on doit avoir fini un groupe de Delta
              if ExpectedRowState <> usUnmodified then
                raise ExxxSaveError.CreateResFmt(@SErrIncoherentDeltaRowStateFmt, [ClassName()]);
            end;
     
          finally
            DeltaCDS.Free();
          end;
        except
          on E: EDBClient do
          begin
            if E.ErrorCode <> ERRBASE_ALC + ERRCODE_DELTAISEMPTY then
              raise;
          end;
        end;
      finally
        FSaving := False;
      end;
    end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  6. #6
    Membre émérite Avatar de edam
    Homme Profil pro
    Développeur Delphi/c++/Omnis
    Inscrit en
    Décembre 2003
    Messages
    1 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Maroc

    Informations professionnelles :
    Activité : Développeur Delphi/c++/Omnis
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 894
    Points : 2 771
    Points
    2 771
    Par défaut
    pour affecter les champs à un clientdataset depuis une une autre TDataset: sur TclientDataset --> click button droits --> charger depuis une table

    normalement, si tu TCDS se connecte à un FDStoredProc, tout les champs seront en lecture seul
    PAS DE DESTIN, C'EST CE QUE NOUS FAISONS

Discussions similaires

  1. [EJB3] Comment utiliser les interfaces locales ?
    Par zarba dans le forum Java EE
    Réponses: 1
    Dernier message: 15/02/2008, 18h12
  2. Déclaration et utilisation de variables locales en pile
    Par johan_b dans le forum x86 32-bits / 64-bits
    Réponses: 2
    Dernier message: 04/02/2008, 09h36
  3. Réponses: 14
    Dernier message: 21/07/2006, 14h43
  4. [Applet] Utilisation sur poste local
    Par stoj dans le forum Applets
    Réponses: 1
    Dernier message: 21/09/2005, 11h58
  5. utilisation de set locale
    Par ColonelHati dans le forum C
    Réponses: 7
    Dernier message: 02/05/2005, 14h25

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