2016-10-05 6 views
0

Ich habe Probleme herauszufinden, wie man neue Datenfelder zu einer alten Datei hinzufügt. Zum Beispiel könnte ein altes Dataset nur ein ID-Feld haben. Später entscheiden wir, dass wir ein ISACTIVE-Feld brauchen. Ich möchte meine ID-only-Daten erneut öffnen und sie dann mit ISACTIVE-Werten erneut speichern. Zum Beispiel:Delphi ClientDataSet - Wie fügt man dem bestehenden Dataset neue Datenfelder hinzu?

CDS := TClientDataset.Create(nil); 
with TIntegerField.Create(CDS) do 
begin 
    FieldName := 'ID'; 
    FieldKind := fkData; 
    DataSet := CDS; 
end; 
CDS.CreateDataSet; 

CDS.Close; 
with TBooleanField.Create(CDS) do 
begin 
    FieldName := 'ISACTIVE'; 
    FieldKind := fkData; 
    DataSet := CDS; 
end; 
CDS.Open; // <--Raises EDatabaseError with message 'Field 'ISACTIVE' not found'. 

ich einen Blick für ähnliche Fragen hatte, fand in der Nähe Ich war einer, der nur zu einem Datensatz mit neuen berechneten Feldern betroffen. Diese Methode funktioniert gut, um ein berechnetes Feld hinzuzufügen.

Im Moment ist die einzige (chaotische) Lösung, die ich mir vorstellen kann, die ID-only-Daten in ein temporäres Dataset zu laden, dann ein neues Dataset mit definierten ID- und ISACTIVE-Feldern zu erstellen und dann das ID-only-Dataset zu durchlaufen und kopieren Sie die Datensätze in den neuen Datensatz.

+0

Ist diese Hilfe Sie ? http://stackoverflow.com/questions/21293186/delphi-change-fields-definitions-of-a-tclientdataset-that-has-data – Graymatter

+0

hmm, yep in gewisser Weise. Es ist eine merkwürdig gestellte Frage, denke ich ?! Wie auch immer, diese Antwort http://stackoverflow.com/a/21295035/6620329 beschreibt, was ich als "unordentliche" Lösung am Ende meiner Frage bezeichnet.Vielleicht ist das die einzige Option, die mir zur Verfügung steht (Bar MartynAs Vorschlag, die Textdatei auf der Festplatte zu bearbeiten). –

Antwort

1

Es gibt eine einfache Möglichkeit, dies zu tun.

Wenn Sie eine CDS mit einem Integer-ID-Feld und ein String 80 Feld Namen, und Sie speichern den Datensatz zu XML, wie in

AFileName := 'C:\Temp\CDSData.Xml'; 
CDS1.SaveToFile(AFileName, dfXML); 

die resultierenden XML-Datei wird wie folgt aussehen (für D7)

<?xml version="1.0" standalone="yes"?> 
<DATAPACKET Version="2.0"> 
    <METADATA> 
    <FIELDS> 
     <FIELD attrname="ID" fieldtype="i4"/> 
     <FIELD attrname="Name" fieldtype="string" WIDTH="80"/> 
    </FIELDS><PARAMS CHANGE_LOG="1 0 4"/> 
    </METADATA> 
    <ROWDATA> 
    <ROW RowState="4" ID="1" Name="one"/> 
    </ROWDATA> 
</DATAPACKET> 

Sie können dann mit MSXML oder Ihrem bevorzugten XML-Prozessor die triviale Änderung vorzunehmen, um zusätzliche FIELD Knoten (s) zum METADATA die CCDS ‚s Datenpaket definieren, das zusätzliche Feld (n) hinzuzufügen. Sie laden dann das CDS aus dem XML neu. Die hinzugefügten Feldwerte sind natürlich NULL. Damit diese Technik funktioniert, dürfen Sie keine dauerhaften TFields auf dem CDS zum Zeitpunkt des erneuten Ladens aus der gespeicherten XML-Datei definiert haben.

Beispielcode:

procedure TForm1.CopyWithAddedFields; 
var 
    SS : TStringStream; 
    XMLDoc : IXmlDomDocument; 
    FieldsNode : IXmlDomNode; 
    FieldElement : IXmlDomElement; 
begin 
    SS := TStringStream.Create(''); 
    try 
    // Save the CDS's current contents in XML format, close it and clear any presistent fields 
    CDS1.SaveToStream(SS, dfXML); 
    CDS1.Close; 
    CDS1.Fields.Clear; 

    // Next create an XML Document object and load the saved dataset into it 
    XMLDoc := CoDomDocument.Create; 
    XMLDoc.LoadXML(SS.DataString); 

    // Find the FIELDS node and add a new FIELD node to it  
    FieldsNode := XMLDoc.selectSingleNode('/DATAPACKET/METADATA/FIELDS'); 
    FieldElement := XMLDoc.createElement('FIELD'); 
    FieldElement.SetAttribute('attrname', 'Active'); 
    FieldElement.SetAttribute('fieldtype', 'boolean'); 
    FieldsNode.appendChild(FieldElement); 

    // Save the XML to the stream 
    SS.Size := 0; 
    SS.WriteString(XmlDoc.xml); 
    SS.Position := 0; 

    // Reload the ClientDataset 
    CDS1.LoadFromStream(SS); 
    finally 
    XMLDoc.Free; 
    SS.Free; 
    end; 
end; 

Offensichtlich könnten Sie die geänderte XML in eine andere CDS laden statt, wenn man wollte.

Natürlich könnten Sie sogar die zusätzlichen FIELD Knoten zum XML hinzufügen, indem Sie sie einfach in eine TStringList laden, wenn Sie bereit wären, eine bestimmte Menge an String-Twiddling durchzuführen.

Fwiw, ich stolperte über diese List beim Versuch, das XML einer CDS zu ändern, um zusätzliche Informationen für jeden ROW Knoten in der XML-Datei aufzunehmen; es stellte sich heraus, dass die LoadFromFile & LoadFromStream Prozesse waren völlig vergessen, die Informationen, die ich hinzugefügt.

+0

Es ist eine gültige Antwort und tatsächlich einfacher zu implementieren, als ich mir vorgestellt habe. Allerdings sehe ich keinen Vorteil gegenüber der oben beschriebenen "chaotischen" Lösung. –

+0

Jeder für sich, denke ich. Der Grund, warum ich diesen Ansatz bevorzuge, ist, dass Sie ihn als eine "Toolbox" -Routine umschreiben, dann brauchen Sie nur ein wenig XML, um die hinzuzufügenden Felder (und optional die anfänglichen Feldwerte) zu definieren. – MartynA

-1

Hm, was falsch mit berechneten Feldern? Könnten Sie bitte erklären, warum Sie sie nicht benutzen wollen?

Ein zusätzlicher Hinweis: Haben Sie versucht, InternalCalc (FieldKind = fkInternalCalc). AFAIK es zu Datenfeld ähnlich verhalten soll (es die Werte innerhalb des Datensatzes Datensatz gespeichert werden)

PS und es besteht keine Notwendigkeit, Daten neu zu laden (schließen und wieder öffnen Datensatz), wenn Sie ein berechnetes Feld Hinzufügen

+0

Dies ist das Ziel: [link] (http://i.imgur.com/4mLIlil.png). Mit anderen Worten, fügen Sie dem zuvor gespeicherten Datensatz neue Daten (neue Felder) hinzu. InternalCalc oder nicht macht keinen Unterschied, und wird auch nicht gespeichert, wenn Sie SaveToFile aufrufen. Warum möchte ich das berechnete Feld nicht verwenden? Es geht um Rückwärtskompatibilität. Es gibt viele gespeicherte "Dateien" in der Wildnis mit alter Struktur. Ich muss den alten Dateien neue Eigenschaften hinzufügen, wenn ein Benutzer sie öffnet und erneut speichert. TClientDataSet macht diese Aufgabe nicht einfach! –

+0

Und eine Randnotiz: Das Hinzufügen eines neuen InternalCalc-Felds zu einem offenen TClientDataSet verursacht mit Sicherheit "EDatabaseError mit der Nachricht 'Diese Operation kann für ein geöffnetes Dataset nicht ausgeführt werden'." –

Verwandte Themen