2010-09-09 56 views
3

我正在创建一个使用TClientDataset作为接收缓冲区的内存数据集。添加数据非常好,但是一旦我开始处理它,我希望能够从数据集中删除该行。调用删除工作 - 排序 - 行/索引仍然可访问,但现在不包含有效信息。Object Pascal:TClientDataset删除

这使得事情,当我处理这个缓冲区它不能保证参赛作品将在事实上被删除,因为有点困难。我宁愿不从第一个条目开始扫描缓冲区并跳过空项目,那么是否有更好的方法可以永久地从数据集中“移除”该项目?我的想法是,它应该像真正的SQL表一样工作,删除行不会留下空记录。

实现此目标的最佳方法是什么?或者我完全使用了错误的组件?

+1

你可以编辑你的问题,并提供演示此问题的代码示例?每次使用Delete时,都会完全删除该行。 – 2010-09-09 13:09:53

+0

我在两个线程中使用该对象,即填充该线程的对象以及处理并从中删除的线程。我在每个中都使用了一个TCriticalSection锁。以下是一些代码:http://pastebin.com/FatuYEwa。现在,try/finally块中的位发生在一个定时器中,并且它在第一次执行时完美运行。但是,第二次调用MyDataset.First之后,它将指向一个不包含有效数据的条目。所有字段根据数据类型是空的(0表示整数,字符串为空字符串等)。此外,RecordCount包含相同数量的条目。 – Diego 2010-09-09 13:53:33

+0

不是你的问题,但看着你的代码,你错过了删除的记录旁边的记录。你的。下一个陈述应该是别的,以避免这种不当行为。 – jachguate 2010-09-10 02:33:17

回答

1

默认情况下客户端的数据集一十个分量的变化“日志”,因为它们也被设计为能够客户端更改发送到远程服务器,即使它们在断开的会话作了(“公文包模式”)。通常,当您将更改应用到远程数据库时,此日志将被“清除”,并且任何其他更改将与您的“本地”副本合并。 如果您不需要它,并且希望直接进行更改,请将LogChanges设置为False。

+0

我试过但我的结果是一样的。这就像删除只是用空值或其他东西覆盖我的条目。 – Diego 2010-09-09 14:16:06

0

关于你的代码的一些评论。

  1. 您正在使用不寻常的方式循环访问您的数据集(使用计数器并仍然使用下一个)。

  2. 我的删除时优选的方向将是从结尾到开头。

  3. 删除后不发布数据集。

我的建议是尝试这样的:

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; 
+3

删除后调用Post会引发异常:'EDatabaseError:无法对已关闭的数据集执行此操作' – 2010-09-09 15:53:55

1

有一些你的代码错误。我为这种情况准备了一个测试应用程序,因为我将在几天内面对多线程环境中的TClientDataSet。我的测试情况下,应用程序没有提出这个问题(德尔福2010更新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来避免现在需要调查的时间。

相关问题