Delphi DataSetProvider BeforeUpdateRecord WideMemoField OldValue丢失

时间:2018-11-14 17:00:25

标签: delphi tdatasetprovider

为我们的数据库应用程序编写一些更改日志,我面临以下问题:在我的DataSetProvider的BeforeUpdateRecord事件中,缺少任何(已修改的)WideMemo字段的OldValue。

很明显,它在我ApplyUpdates之前就存在于ClientDataSet中,因此在创建增量或由DataSetProvider对其进行解压缩时,它会被丢弃。

如何获得该价值?

只是在相关的情况下,这是我使用的组件

客户端: TClientDataSet TDataSource

服务器端: TIB查询 TDataSetProvider

连接到Firebird数据库

Delphi Tokyo和Datasnap

干杯!

1 个答案:

答案 0 :(得分:1)

我想我会将其发布为新答案,作为对我现在所理解的新答案 从OP的评论中可以看出他的具体问题。 OP想要做的是捕获数据集字段的OldValue 服务器端,即IBQuery,而不是TDatasetProvider的客户端。一旦确定OP已经看到这个答案,我将把我以前的答案记下来。

考虑以下代码:

type
  TMyIBQuery = Class(TIBQuery)

  end;

procedure TForm1.IBQuery1BeforePost(DataSet: TDataSet);
var
  OldValue : Variant;
  PrvState : TDataSetState;
begin
  PrvState := IBQuery1.State;
  try
    TMyIBQuery(IBQuery1).SetTempState(dsOldValue);
    OldValue := IBQuery1.FieldByName('AValue').OldValue;
    Memo1.Lines.Add('OldValue: ' + OldValue);
  finally
    TMyIBQuery(IBQuery1).RestoreState(PrvState);
  end;
end;

如果DataSetProvider具有默认设置,则在以下情况下调用IBQuery1BeforePost 在连接到DSP的CDS上调用ApplyUpdates,因为应用过程 更新会绕过正常的IBQuery编辑过程。

但是,如果将DSP的ResolveToDataSet属性设置为True,则IBQuery1BeforePost 是否执行并正确提取AValue字段的OldValue,它是WideMemo 我的设置中的字段。当然,执行AfterPost代码的原因是当ResolveToDataSet时 设置为True,则使用通常的IBQuery编辑方法。

更新:以下是我在评论中提到的项目摘录:

代码提取

type
  TForm1 = class(TForm)
    DataSource1: TDataSource;
    DBGrid1: TDBGrid;
    DBNavigator1: TDBNavigator;
    IBDatabase1: TIBDatabase;
    IBTransaction1: TIBTransaction;
    Memo1: TMemo;
    IBEvents1: TIBEvents;
    IBQuery1: TIBQuery;
    IBUpdateSQL1: TIBUpdateSQL;
    LblTrans: TLabel;
    Timer1: TTimer;
    IBQuery1ID: TIntegerField;
    IBQuery1ANAME: TIBStringField;
    IBQuery1AVALUE: TWideMemoField;
    DBMemo1: TDBMemo;
    DataSetProvider1: TDataSetProvider;
    CDS1: TClientDataSet;
    [...]
  end;

[...]
procedure TForm1.FormDestroy(Sender: TObject);
begin
  IBQuery1.Close;
  if IBTransaction1.InTransaction then
    IBTransaction1.Commit;
  if IBTransaction1.Active then
    IBTransaction1.Active := False;
  if IBEvents1.Registered then
    IBEvents1.Registered := False;
  IBDatabase1.Connected := False;
end;

procedure TForm1.RefreshTable2;
begin
  if IBQuery1.Modified then
    IBDatabase1.ApplyUpdates([IBQuery1]);
  RefreshDS;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  UpdateTransLabel;
end;

procedure TForm1.UpdateTransLabel;
begin
  if IBTransaction1.Active then
    lblTrans.Caption := 'Trans Active'
  else
    lblTrans.Caption := 'Trans Inactive';
end;

procedure TForm1.CDS1AfterPost(DataSet: TDataSet);
begin
  CDS1.ApplyUpdates(0);
end;

procedure TForm1.DBNavigator1BeforeAction(Sender: TObject; Button:
    TNavigateBtn);
begin
 if IBQuery1.CanModify then
   lblTrans.Caption := lblTrans.Caption + ' RW'
 else
   lblTrans.Caption := lblTrans.Caption + ' RO'
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Caption := ExtractFileName(Application.ExeName) + ' / ' + IBDatabase1.Params.Values['user_name'];
  IBQuery1.Open;
end;

procedure TForm1.IBDatabase1AfterConnect(Sender: TObject);
begin
  IBDatabase1.Connected := True;
  IBEvents1.Registered := True;
end;

procedure TForm1.IBEvents1EventAlert(Sender: TObject; EventName: string;
    EventCount: Integer; var CancelAlerts: Boolean);
begin
  Memo1.Lines.Add('Evt: (' + IntToStr(EventCount) + ') ' + EventName);
end;

procedure TForm1.CommitAndRefresh;
begin
  IBTransaction1.CommitRetaining;
  RefreshTable2;
end;

procedure TForm1.IBQuery1AfterDelete(DataSet: TDataSet);
begin
  CommitAndRefresh;
end;

procedure TForm1.IBQuery1AfterPost(DataSet: TDataSet);
begin
  CommitAndRefresh;
end;

type
  TMyIBQuery = Class(TIBQuery)
  end;

procedure TForm1.IBQuery1BeforePost(DataSet: TDataSet);
var
  OldValue : Variant;
  PrvState : TDataSetState;
begin
  PrvState := IBQuery1.State;
  try
    TMyIBQuery(IBQuery1).SetTempState(dsOldValue);
    OldValue := IBQuery1.FieldByName('AValue').OldValue;
    Memo1.Lines.Add('OldValue: ' + OldValue);
  finally
    TMyIBQuery(IBQuery1).RestoreState(PrvState);
  end;
end;

procedure TForm1.IBQuery1UpdateError(DataSet: TDataSet; E: EDatabaseError;
    UpdateKind: TUpdateKind; var UpdateAction: TIBUpdateAction);
begin
  UpdateAction := UpdateErrorForm.HandleError(DataSet, E, UpdateKind);
end;

procedure TForm1.RefreshDS;
var
  BM : TBookmark;
begin
  BM := IBQuery1.GetBookmark;
  try
    IBQuery1.Close;
    IBQuery1.Open;
  finally
    if IBQuery1.BookmarkValid(BM) then
      IBQuery1.GotoBookmark((BM));
    IBQuery1.FreeBookmark(BM);
  end;
end;

end.

DFM提取

object Form1: TForm1
  [...]
  object LblTrans: TLabel
    [...]
    Alignment = taRightJustify
    Caption = '???'
  end
  object DBGrid1: TDBGrid
    [...]
    DataSource = DataSource1
    Columns = <
      item
        Expanded = False
        FieldName = 'ID'
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'ANAME'
        Width = 80
        Visible = True
      end
      item
        Expanded = False
        FieldName = 'AVALUE'
        Width = 200
        Visible = True
      end>
  end
  object DBNavigator1: TDBNavigator
    [...]
    DataSource = DataSource1
  end
  object Memo1: TMemo
    [...]
  end
  object DBMemo1: TDBMemo
    [...]
    DataField = 'AVALUE'
    DataSource = DataSource1
  end
  object DataSource1: TDataSource
    DataSet = CDS1
  end
  object IBDatabase1: TIBDatabase
    Connected = True
    DatabaseName = 'LocalHost:D:\Delphi\Interbase\Databases\MA.GDB'
    Params.Strings = (
      'user_name=SYSDBA'
      'password=masterkey')
    LoginPrompt = False
    DefaultTransaction = IBTransaction1
    ServerType = 'IBServer'
    TraceFlags = [tfQPrepare, tfQExecute, tfQFetch, tfError, tfStmt, tfConnect, tfTransact, tfBlob, tfService, tfMisc]
    AfterConnect = IBDatabase1AfterConnect
  end
  object IBTransaction1: TIBTransaction
    Active = True
    DefaultDatabase = IBDatabase1
    DefaultAction = TACommitRetaining
    Params.Strings = (
      'read_committed'
      'rec_version'
      'nowait')
  end
  object IBEvents1: TIBEvents
    AutoRegister = False
    Database = IBDatabase1
    Events.Strings = (
      'NewRow'
      'RowDeleted'
      'RowUpdated')
    Registered = False
    OnEventAlert = IBEvents1EventAlert
  end
  object IBQuery1: TIBQuery
    Database = IBDatabase1
    Transaction = IBTransaction1
    AfterDelete = IBQuery1AfterDelete
    AfterPost = IBQuery1AfterPost
    BeforePost = IBQuery1BeforePost
    BufferChunks = 1000
    CachedUpdates = False
    ParamCheck = True
    SQL.Strings = (
      'select * from table2')
    UpdateObject = IBUpdateSQL1
    Left = 112
      FieldName = 'ID'
      Origin = '"TABLE2"."ID"'
      ProviderFlags = [pfInUpdate, pfInWhere, pfInKey]
    end
    object IBQuery1ANAME: TIBStringField
      FieldName = 'ANAME'
      Origin = '"TABLE2"."ANAME"'
      Size = 80
    end
    object IBQuery1AVALUE: TWideMemoField
      FieldName = 'AVALUE'
      Origin = '"TABLE2"."AVALUE"'
      ProviderFlags = [pfInUpdate]
      BlobType = ftWideMemo
      Size = 8
    end
  end
  object IBUpdateSQL1: TIBUpdateSQL
    RefreshSQL.Strings = (
      'Select '
      'from table2 '
      'where'
      '  ID = :ID')
    ModifySQL.Strings = (
      'update table2'
      'set'
      '  ID = :ID,'
      '  ANAME = :ANAME,'
      '  AVALUE = :AVALUE'
      'where'
      '  ID = :OLD_ID')
    InsertSQL.Strings = (
      'insert into table2'
      '  (ID, ANAME, AVALUE)'
      'values'
      '  (:ID, :ANAME, :AVALUE)')
    DeleteSQL.Strings = (
      'delete from table2'
      'where'
      '  ID = :OLD_ID')
  end
  object Timer1: TTimer
    OnTimer = Timer1Timer
  end
  object DataSetProvider1: TDataSetProvider
    DataSet = IBQuery1
    ResolveToDataSet = True
  end
  object CDS1: TClientDataSet
    Active = True
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider1'
    AfterPost = CDS1AfterPost
  end
end

表DDL

CREATE TABLE "TABLE2"
(
  "ID"  INTEGER NOT NULL,
  "ANAME"   VARCHAR(80),
  "AVALUE"  BLOB SUB_TYPE TEXT SEGMENT SIZE 80
);

CREATE TRIGGER "GETTABLE2ID" FOR "TABLE2"
ACTIVE BEFORE INSERT POSITION 0
AS
begin
  new.ID = gen_id("TABLE2ID", 1);
  POST_EVENT('NewRow');
end

CREATE TRIGGER "UPDATETABLE2ROW" FOR "TABLE2"
ACTIVE AFTER UPDATE POSITION 0
AS
begin
  POST_EVENT('T2 RowUpdated');
end

COMMIT WORK