Frage

Ich habe letzte Woche etwas beobachtet, das ich nicht erwartet hatte, und werde unten beschreiben. Ich bin gespannt, warum dies passiert. Ist es für die TDataset -Klasse etwas Inneres, ein Artefakt des Tdbgrids oder etwas anderes?

Die Reihenfolge der Felder in einem offenen ClientDataset änderte sich. Insbesondere habe ich einen ClientDataset in Code erstellt, indem ich erstellte kennzeichnendes aufgerufen habe, nachdem er seine Struktur mithilfe von FieldDefs definiert habe. Das erste Feld in der Struktur dieses ClientDataset war ein Datumsfeld mit dem Namen StartofWeek. Nur wenige Augenblicke später war Code, den ich auch geschrieben hatte, wodurch angenommen wurde, dass sich das Feld StartofWeek in der Nulleth -Position, ClientDataset.Fields [0] befand, fehlgeschlagen, da das Feld StartofWeek nicht mehr das erste Feld im ClientDataset war.

Nach einigen Untersuchungen erfuhr ich, dass es möglich war, dass jedes einzelne Feld im ClientDataset zu einem bestimmten Zeitpunkt in einer Position in einer anderen Position erscheinen könnte, die sich zum Zeitpunkt der Erstellung des ClientDatasets von der ursprünglichen Struktur unterscheidet. Ich war mir nicht bewusst, dass dies passieren könnte, und eine Suche bei Google hat auch diesen Effekt nicht erwähnt.

Was passiert ist, war nicht Magie. Die Felder änderten sich nicht von sich aus und haben sich auch nicht anhand von allem geändert, was ich in meinem Code getan habe. Was dazu führte, dass die Felder physisch die Position im ClientDataset physisch ändern schienen, war, dass der Benutzer die Reihenfolge der Spalten in einem DBGrid geändert hatte, an den der ClientDataset angehängt wurde (natürlich über eine DataSource -Komponente). Ich habe diesen Effekt in Delphi 7, Delphi 2007 und Delphi 2010 wiederholt.

Ich habe eine sehr einfache Delphi -Anwendung erstellt, die diesen Effekt demonstriert. Es besteht aus einer einzelnen Form mit einem DBGrid, einer DataSource, zwei ClientDatasets und zwei Schaltflächen. Der OnCreate -Event -Handler dieser Form sieht aus wie die folgenden

procedure TForm1.FormCreate(Sender: TObject);
begin
  with ClientDataSet1.FieldDefs do
  begin
    Clear;
    Add('StartOfWeek', ftDate);
    Add('Label', ftString, 30);
    Add('Count', ftInteger);
    Add('Active', ftBoolean);
  end;
  ClientDataSet1.CreateDataSet;
end;

Button1, die als "ClientDataset -Struktur" bezeichnet wird, enthält den folgenden Onclick -Event -Handler.

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    sl.Add('The Structure of ' + ClientDataSet1.Name);
    sl.Add('- - - - - - - - - - - - - - - - - ');
    for i := 0 to ClientDataSet1.FieldCount - 1 do
      sl.Add(ClientDataSet1.Fields[i].FieldName);
    ShowMessage(sl.Text);
  finally
    sl.Free;
  end;
end;

Führen Sie diese Anwendung aus und klicken Sie auf die Schaltfläche "STOUS CLientDATASET". Sie sollten so etwas sehen, das hier gezeigt wird:

The Structure of ClientDataSet1
- - - - - - - - - - - - - - - - - 
StartOfWeek
Label
Count
Active

Ziehen Sie als nächstes die Spalten des DBGrid, um die Anzeigereihenfolge der Felder neu zu ordnen. Klicken Sie erneut auf die Schaltfläche clientDataset -Struktur. Diesmal sehen Sie etwas Ähnliches wie hier gezeigt:

The Structure of ClientDataSet1
- - - - - - - - - - - - - - - - - 
Label
StartOfWeek
Active
Count

Was an diesem Beispiel bemerkenswert ist, ist, dass die Spalten der DBGrid bewegt werden, aber es gibt einen offensichtlichen Einfluss auf die Position der Felder im ClientDataset, so dass das Feld, das sich in der ClientDataset befand. Der Punkt ist nicht unbedingt Momente später da. Und leider ist dies nicht eindeutig ein ClientDataset -Problem. Ich habe den gleichen Test mit BDE-basierten TTables und Ado-basierten Adotables durchgeführt und den gleichen Effekt erhalten.

Wenn Sie sich nie auf die Felder in Ihrem ClientDataset beziehen müssen, die in einem DBGrid angezeigt werden, müssen Sie sich keine Sorgen um diesen Effekt machen. Für den Rest von Ihnen kann ich mir mehrere Lösungen vorstellen.

Der einfachste, wenn auch nicht notwendige Weg, um dieses Problem zu vermeiden, besteht darin, den Benutzer daran zu hindern, Felder in einem DBGrid neu zu ordnen. Dies kann durch Entfernen der DGresizecolumn -Flagge aus der Optionseigenschaft des DBGrids erfolgen. Obwohl dieser Ansatz effektiv ist, wird aus Sicht des Benutzers eine potenziell wertvolle Anzeigeoption beseitigt. Darüber hinaus schränkt das Entfernen dieses Flags nicht nur die Neuordnung der Spalten ein, sondern verhindert auch die Spaltenänderung. (Um zu erfahren http://delphi.about.com/od/adptips2005/a/bltip0105_2.htm.)

Die zweite Problemumgehung besteht darin, zu vermeiden, dass sich die Felder eines Datensatzes basierend auf ihrer wörtlichen Position beziehen (da dies die Essenz des Problems ist). Wenn Sie sich auf das Feld Count verweisen müssen, verwenden Sie keine Datensatz.Fields [2]. Solange Sie den Namen des Feldes kennen, können Sie so etwas wie DataSet verwenden.

Es gibt jedoch einen ziemlich großen Nachteil bei der Verwendung von FieldbyName. Insbesondere identifiziert diese Methode das Feld, indem es durch die Fields -Eigenschaft des Datensatzes iteriert und nach einer Übereinstimmung basierend auf dem Feldnamen sucht. Da dies jedes Mal, wenn Sie FeldbyName aufrufen, dies geschieht, ist dies eine Methode, die in Situationen vermieden werden sollte, in denen das Feld viele Male verwiesen werden muss, z. B. in einer Schleife, die in einem großen Datensatz navigiert.

Wenn Sie sich wiederholt auf das Feld beziehen müssen (und eine große Anzahl von Male), sollten Sie so etwas wie das folgende Code -Snippet verwenden:

var
  CountField: TIntegerField;
  Sum: Integer;
begin
  Sum := 0;
  CountField := TIntegerField(ClientDataSet1.FieldByName('Count'));
  ClientDataSet1.DisableControls;  //assuming we're attached to a DBGrid
  try
    ClientDataSet1.First;
    while not ClientDataSet1.EOF do
    begin
      Sum := Sum + CountField.AsInteger;
      ClientDataSet1.Next;
    end;
  finally
    ClientDataSet1.EnableControls;
  end;

Es gibt eine dritte Lösung, aber dies ist nur verfügbar, wenn Ihr Datensatz ein ClientDataset ist, wie das in meinem ursprünglichen Beispiel. In diesen Situationen können Sie einen Klon des ursprünglichen ClientDatasets erstellen, und es wird die ursprüngliche Struktur haben. Infolgedessen befindet sich das Feld, das in der Position von Zeroeth erstellt wurde, weiterhin in dieser Position, unabhängig davon, was ein Benutzer einem DBGrid angetan hat, der die ClientDatasets -Daten anzeigt.

Dies wird im folgenden Code demonstriert, der mit dem Onclick -Event -Handler der Schaltfläche mit der mitgeklonten Clientdataset -Struktur der SHOW -SHOW -DESTECLE verbunden ist.

procedure TForm1.Button2Click(Sender: TObject);
var
  sl: TStringList;
  i: Integer;
  CloneClientDataSet: TClientDataSet;
begin
  CloneClientDataSet := TClientDataSet.Create(nil);
  try
    CloneClientDataSet.CloneCursor(ClientDataSet1, True);
    sl := TStringList.Create;
    try
      sl.Add('The Structure of ' + CloneClientDataSet.Name);
      sl.Add('- - - - - - - - - - - - - - - - - ');
      for i := 0 to CloneClientDataSet.FieldCount - 1 do
        sl.Add(CloneClientDataSet.Fields[i].FieldName);
      ShowMessage(sl.Text);
    finally
      sl.Free;
    end;
  finally
    CloneClientDataSet.Free;
  end;
end;

Wenn Sie dieses Projekt ausführen und auf die Schaltfläche mit der angegebenen STEC -Clientdataset -Struktur klicken, erhalten Sie immer die wahre Struktur des ClientDataset, wie hier gezeigt

The Structure of ClientDataSet1
- - - - - - - - - - - - - - - - - 
StartOfWeek
Label
Count
Active

Nachtrag:

Es ist wichtig zu beachten, dass die tatsächliche Struktur der zugrunde liegenden Daten nicht beeinflusst wird. Wenn Sie nach Ändern der Reihenfolge der Spalten in einem dbGrid die SavetOfile -Methode des ClientDatasets aufrufen, ist die gespeicherte Struktur die ursprüngliche (echte interne) Struktur. Wenn Sie die Dateneigenschaft eines ClientDatasets auf einen anderen kopieren, zeigt der Zielclientdataset auch die wahre Struktur (die dem Effekt ähnelt, der beobachtet wird, wenn ein Quellclientdataset geklont wird).

In ähnlicher Weise beeinflussen Änderungen an den Spaltenordnungen von DBGrids, die an andere getestete Datensätze, einschließlich TTABLE und ADOTABLE, gebunden sind, die Struktur der zugrunde liegenden Tabellen nicht wirklich beeinflussen. Zum Beispiel eine TTABLE, die Daten vom Kunden -DB -Beispielparadox -Tabelle anzeigt, die mit Delphi die Struktur dieser Tabelle nicht ändern (auch nicht erwarten).

Was wir aus diesen Beobachtungen schließen können, ist, dass die interne Struktur des Datensatzes selbst intakt bleibt. Infolgedessen muss ich davon ausgehen, dass es irgendwo eine sekundäre Darstellung der Struktur des Datensatzes gibt. Und es muss entweder mit dem Datensatz verknüpft sein (was scheinbar übertrieben zu sein scheint, da nicht alle Verwendungen eines Datensatzes dies benötigen), die dem DBGrid zugeordnet sind (was sinnvoller ist, da das DBGrid diese Funktion verwendet, aber nicht ist unterstützt durch die Beobachtung, dass die T -Field -Neuordnung mit dem Datensatz selbst bestehen scheint) oder etwas anderes ist.

Eine weitere Alternative ist, dass der Effekt mit dem TGridDatalink verbunden ist, der die Klasse ist, die mehrstufige Kontrollen (wie DBGrids) ihr Datenbewusstsein verleiht. Ich bin jedoch geneigt, diese Erklärung abzulehnen, da diese Klasse mit dem Netz und nicht dem Datensatz zugeordnet ist, da der Effekt an den Datensatzklassen selbst bestehen scheint.

Das bringt mich zurück zur ursprünglichen Frage. Ist diese Wirkung etwas Inneres für die Tdataset -Klasse, ein Artefakt des Tdbgrids oder etwas anderes?

Erlauben Sie mir auch, hier etwas zu betonen, das ich zu einem der folgenden Kommentare hinzugefügt habe. Vor allem ist mein Beitrag so konzipiert, dass Entwickler sich darauf aufmerksam machen, dass sich die Reihenfolge ihrer TFields möglicherweise auch ändern kann, wenn sie dbgrids verwenden, deren Spaltenbestellungen geändert werden können. Dieses Artefakt kann intermittierende und schwerwiegende Fehler einführen, die sehr schwer zu identifizieren und zu beheben können. Und nein, ich glaube nicht, dass dies ein Delphi -Fehler ist. Ich vermute, dass alles funktioniert, da es für die Arbeit entwickelt wurde. Es ist nur so, dass viele von uns nicht wussten, dass dieses Verhalten stattfand. Jetzt wissen wir.

War es hilfreich?

Lösung

Anscheinend ist das Verhalten von Design. Tatsächlich hängt es nicht mit dem DBGrid zusammen. Es ist lediglich ein Nebeneffekt einer Spalte, die einen Feldindex einstellt. Zum Beispiel diese Aussage,

ClientDataset1.Fields [0] .Index: = 1;

führt dazu, dass die Ausgabe der Taste "ClientDataset -Struktur anzeigen" entsprechend geändert wird, entweder gibt es ein Raster oder nicht. Die Dokumentation für tfield.index States;

"Ändern Sie die Reihenfolge der Position eines Feldes im Datensatz, indem Sie den Wert des Index ändern. Ändern des Indexwerts wirkt sich auf die Reihenfolge aus, in der Felder in Datengitter angezeigt werden, jedoch nicht die Position der Felder in physischen Datenbanktabellen."

Man sollte zu dem Schluss kommen, dass das Gegenteil auch wahr sein sollte, und die Änderung der Reihenfolge der Felder in einem Raster sollte dazu führen, dass die Feldindizes geändert werden.


Der Code, der dies verursacht, ist in tcolumn.setIndex. Tcustomdbgrid.columnmoved legt einen neuen Index für die bewegte Spalte fest und tcolumn.setIndex legt den neuen Index für das Feld dieser Spalte fest.

procedure TColumn.SetIndex(Value: Integer);
[...]
        if (Col <> nil) then
        begin
          Fld := Col.Field;
          if Assigned(Fld) then
            Field.Index := Fld.Index;
        end;
[...]

Andere Tipps

Wodzu hat eine Lösung für das nachgeordnete Feldproblem veröffentlicht, das spezifisch für ADO -Datensatz war, aber er führte mich zu einer ähnlichen Lösung und für alle Datensätze (unabhängig davon, ob sie ordnungsgemäß in der implementierten alle Datensätze sind ein weiteres Problem). Beachten Sie, dass weder diese Antwort noch Wodzu eine Antwort auf die ursprüngliche Frage sind. Stattdessen ist es eine Lösung für das festgestellte Problem, während die Frage sich darauf bezieht, wo dieses Artefakt stammt.

Die Lösung, zu der mich Wodzu -Lösung mich ließ, war FeldbyNumber und es ist eine Methode der Fields -Eigenschaft. Die Verwendung von Feldbynummer hat zwei interessante Aspekte. Zunächst müssen Sie seine Referenz mit der Fields -Eigenschaft Ihres Datensatzes qualifizieren. Zweitens ist FieldByNumber im Gegensatz zum Fields-Array, das einen Null-basierten Indexer nimmt, eine Methode, die einen einbasierten Parameter annimmt, um die Position des Tfield anzuzeigen, auf das Sie sich verweisen möchten.

Das Folgende ist eine aktualisierte Version des Button1 -Event -Handlers, den ich in meiner ursprünglichen Frage gepostet habe. Diese Version verwendet FeldByNumber.

procedure TForm1.Button1Click(Sender: TObject);
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    sl.Add('The Structure of ' + ClientDataSet1.Name +
      ' using FieldByNumber');
    sl.Add('- - - - - - - - - - - - - - - - - ');
    for i := 0 to ClientDataSet1.FieldCount - 1 do
      sl.Add(ClientDataSet1.Fields.FieldByNumber(i + 1).FieldName);
    ShowMessage(sl.Text);
  finally
    sl.Free;
  end;
end;

Für das Beispielprojekt erzeugt dieser Code die folgende Ausgabe, unabhängig von der Ausrichtung der Spalten im zugehörigen DBGrid:

The Structure of ClientDataSet1 using FieldByNumber
- - - - - - - - - - - - - - - - - 
StartOfWeek
Label
Count
Active

Beachten Sie, dass der Verweis auf das zugrunde liegende Tfield eine Feldbynumme erforderte, um unter Bezugnahme auf Felder qualifiziert zu werden. Darüber hinaus muss der Parameter für diese Methode innerhalb des 1 bis zum Datensatz liegen. FieldCount -Bereich. Um sich auf das erste Feld im Datensatz zu beziehen, verwenden Sie den folgenden Code:

ClientDataSet1.Fields.FieldByNumber(1)

Wie das Fields -Array gibt FieldByNumber eine tfield Referenz zurück. Wenn Sie sich daher auf eine Methode beziehen möchten, die für eine bestimmte Tfield -Klasse spezifisch ist, müssen Sie den zurückgegebenen Wert an die entsprechende Klasse geben. Um beispielsweise den Inhalt eines Tblobfield in einer Datei zu speichern, müssen Sie möglicherweise so etwas wie den folgenden Code tun:

TBlobField(MyDataSet.Fields.FieldByNumber(6)).SaveToFile('c:\mypic.jpg');

Beachten Sie, dass ich nicht vorschlägt, dass Sie mit ganzzahligen Literalen auf TFields in einem Datensatz verweisen sollten. Persönlich ist die Verwendung einer Tfield -Variablen, die durch einen einmaligen Anruf bei FeldbyName initialisiert wird, lesbarer und immun gegen Änderungen in der physischen Reihenfolge der Struktur einer Tabelle (obwohl sie nicht immun gegen Änderungen in den Namen Ihrer Felder!).

Wenn Sie jedoch Datensätze mit DBGrids zugeordnet sind, deren Spalten neu angeordnet werden können, und auf die Felder dieser Datensätze mit ganzzahligen Literalen als Indexer des Fields -Arrays verweisen, möchten Sie möglicherweise in Betracht ziehen, Ihren Code zu konvertieren, um die Datensatz zu verwenden.Fields.Fieldbyname -Methode .

Cary Ich glaube, ich habe eine Lösung für dieses Problem gefunden. Anstatt VCL -Wrapper -Felder zu verwenden, müssen wir eine interne Feldereigenschaft des Recordset COM -Objekts verwenden.

Hier ist, wie es referenziert werden sollte:

qry.Recordset.Fields.Item[0].Value

Diese Felder sind nicht von dem Verhalten betroffen, das Sie zuvor beschrieben haben. So können wir uns immer noch auf die Felder nach ihrem Index beziehen.

Testen Sie dies und sagen Sie mir, was das Ergebnis war. Es hat für mich funktioniert.

Bearbeiten:

Natürlich funktioniert es nur für Ado -Komponenten, nicht für den TclientDataset ...

Edit2:

Cary Ich weiß nicht, ob dies eine Antwort auf Ihre Frage ist, aber ich habe Leute in die Embarcadero -Foren gedrängt, und Wayne Niddery gab mir eine ziemlich detaillierte Antwort auf all diese Fields -Bewegungen.

Um es kurz zu machen: Wenn Sie Ihre Spalten in TDBGrid explizit definieren, bewegen sich die Feldindizes nicht! Haben Sie jetzt etwas mehr Sinn, nicht wahr?

Lesen Sie hier den vollständigen Thread:https://forums.embarcadero.com/post!reply.jspa?messageId=197287

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top