Object Pascal: TClientDataset Removal

I am creating a dataset in memory using TClientDataset for use as a receive buffer. Adding data is great, but as soon as I get down to processing it, I want to remove the row from the dataset. Calling delete operations - view - row / index is still available, but now does not contain reliable information.

This makes it a little difficult, because when I process this buffer, it does not guarantee that the records will be actually deleted. I would prefer not to start scanning the buffer from the first record and skip empty elements, so is there a better way to permanently "delete" an element from a dataset? My idea was that it should work like an actual SQL table where deleting a row leaves no empty records.

What is the best way to achieve this, or am I using the wrong component entirely?

+3
source share
3 answers

By default, client datasets process the change log because they are also designed to send changes on the client side to the remote server, even if they were made in a disconnected session (β€œportfolio model”). Usually this log is "cleared" when you apply the changes to the remote db, and any other changes are merged with your "local" copy. Set LogChanges to False if you do not need it and want the changes to be made directly.

+1
source

- . , TClientDataSet . (Delphi 2010 Update 5)

... :

DFM:

object Form2: TForm2
  Left = 0
  Top = 0
  Caption = 'Form2'
  ClientHeight = 337
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnClose = FormClose
  PixelsPerInch = 96
  TextHeight = 13
  object Memo1: TMemo
    Left = 8
    Top = 8
    Width = 257
    Height = 321
    Lines.Strings = (
      'Memo1')
    TabOrder = 0
  end
  object Button1: TButton
    Left = 271
    Top = 8
    Width = 170
    Height = 25
    Caption = 'Start'
    TabOrder = 1
    OnClick = Button1Click
  end
  object cdsTest: TClientDataSet
    Aggregates = <>
    Params = <>
    Left = 584
    Top = 32
    object cdsTestNumber: TIntegerField
      FieldName = 'Number'
    end
  end
  object tToMemo: TTimer
    Enabled = False
    Interval = 500
    OnTimer = tToMemoTimer
    Left = 376
    Top = 144
  end
end

pas :

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, DB, DBClient, SyncObjs, ExtCtrls;

type
  TWriterThread = class(TThread)
  private
    FDataSet: TClientDataSet;
    //FWriteLock: TMultiReadExclusiveWriteSynchronizer;
    FLock: TCriticalSection;
  public
    constructor Create(ADataSet: TClientDataSet; ALock: TCriticalSection);
    procedure Execute; override;
  end;

  TDeleterThread = class(TThread)
  private
    FDataSet: TClientDataSet;
    //FWriteLock: TMultiReadExclusiveWriteSynchronizer;
    FLock: TCriticalSection;
  public
    constructor Create(ADataSet: TClientDataSet; ALock: TCriticalSection);
    procedure Execute; override;
  end;

  TForm2 = class(TForm)
    cdsTest: TClientDataSet;
    Memo1: TMemo;
    cdsTestNumber: TIntegerField;
    Button1: TButton;
    tToMemo: TTimer;
    procedure Button1Click(Sender: TObject);
    procedure tToMemoTimer(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
    FLock: TCriticalSection;
    FWriterThread: TWriterThread;
    FDeleterThread: TDeleterThread;
    procedure cdsToMemo;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  Button1.Enabled := False;
  cdsTest.CreateDataSet;
  cdsTest.LogChanges := False;
  FLock := TCriticalSection.Create;
  tToMemo.Enabled := True;
  FWriterThread := TWriterThread.Create(cdsTest, FLock);
  FDeleterThread := TDeleterThread.Create(cdsTest, FLock);
end;

{ TWriterThread }

constructor TWriterThread.Create(ADataSet: TClientDataSet;
  ALock: TCriticalSection);
begin
  inherited Create(False);
  FDataSet := ADataSet;
  FLock := ALock;
end;

procedure TWriterThread.Execute;
var
  I: Integer;
begin
  inherited;
  I := 0;
  while not Terminated do
  begin
    FLock.Enter;
    try
      Inc(I);
      FDataSet.AppendRecord([I]);
    finally
      FLock.Leave;
    end;
    Sleep(500);  //a new record aproximately each half second
  end;
end;

{ TDeleterThread }

constructor TDeleterThread.Create(ADataSet: TClientDataSet;
  ALock: TCriticalSection);
begin
  inherited Create(False);
  FDataSet := ADataSet;
  FLock := ALock;
end;

procedure TDeleterThread.Execute;
const
  MaxRecords = 100;
var
  ProcessedRecords: Integer;
begin
  inherited;
  while not Terminated do
  begin
    Sleep(3000);  //delete records aproximately every 3 seconds
    FLock.Enter;
    try
      FDataSet.First;
      ProcessedRecords := 0;
      while (not FDataSet.Eof) and (ProcessedRecords < MaxRecords) do
      begin
        Inc(ProcessedRecords);
        if Odd(FDataSet.Fields[0].AsInteger) then
          FDataSet.Delete
        else
          FDataSet.Next;
      end;
    finally
      FLock.Leave;
    end;
  end;
end;

procedure TForm2.cdsToMemo;
begin
  FLock.Enter;
  try
    Memo1.Lines.BeginUpdate;
    try
      Memo1.Lines.Clear;
      cdsTest.First;
      while not cdsTest.Eof do
      begin
        Memo1.Lines.Add(cdsTestNumber.AsString);
        cdsTest.Next;
      end;
    finally
      Memo1.Lines.EndUpdate;
    end;
  finally
    FLock.Leave;
  end;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  tToMemo.Enabled := False;
  if cdsTest.Active then
  begin
    FDeleterThread.Terminate;
    FDeleterThread.WaitFor;
    FWriterThread.Terminate;
    FWriterThread.WaitFor;
  end;
end;

procedure TForm2.tToMemoTimer(Sender: TObject);
begin
  tToMemo.Enabled := False;
  cdsToMemo;
  tToMemo.Enabled := True;
end;

end.

, , , . - , .

: TMultiReadExclusiveWriteSynchronizer, , ReadAccess WriteAccess, CriticalSection, , .

+1

.

  • ( ).

  • My preferred direction when deleting will be from end to start.

  • You do not publish your dataset after deletion.

My suggestion would be to try something like this:

MyDataSet.RecNo:= 99
while not MyDataSet.Bof do
begin
  fD1 := MyDataset.FieldByName('Field1').AsInteger;
  fD2 := MyDataset.FieldByName('Field2').AsInteger;
  fD3 := MyDataset.FieldByName('Field3').AsInteger;

  if someCondition then
    MyDataset.Delete;

  MyDataSet.Post;

  MyDataset.Previous;
end;
0
source

Source: https://habr.com/ru/post/1763978/


All Articles