Delphi DataSetProvider BeforeUpdateRecord WideMemoField OldValue отсутствует - PullRequest
0 голосов
/ 14 ноября 2018

Запись небольшого журнала изменений для нашего приложения БД, и я сталкиваюсь со следующей проблемой: в событии BeforeUpdateRecord моего DataSetProvider отсутствует OldValue любого (измененного) поля WideMemo.

Этоприсутствует в ClientDataSet до того, как я применю ApplyUpdates, очевидно, поэтому где-то при создании дельты или ее распаковке с помощью DataSetProvider она сбрасывается.

Как мне получить это значение?

Просто весли это уместно, вот компоненты, которые я использую

На стороне клиента: TClientDataSet TDataSource

На стороне сервера: TIBQuery TDataSetProvider

Подключение к базе данных Firebird

Delphi Tokyo and Datasnap

Приветствия!

1 Ответ

0 голосов
/ 15 ноября 2018

Я думал, что выложу это как новый ответ как новый ответ на то, что я теперь понимаю из комментариев ОП, чтобы быть его конкретной проблемой. ОП хочет сделать, чтобы поймать OldValue поля набора данных на сторона сервера , то есть на стороне IBQuery, а не на стороне клиента TDatasetProvider. Я уберу свой предыдущий ответ, как только убедился, что ОП его видел.

Рассмотрим следующий код:

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 - это , а не , вызываемый при ApplyUpdates вызывается на CDS, подключенной к DSP, потому что процесс подачи заявки Обновления обходят обычный процесс редактирования IBQuery.

Однако, если вы установите для свойства ResolveToDataSet DSP значение True, IBQuery1BeforePost Выполняет и правильно извлекает OldValue поля AValue, которое является WideMemo поле в моей настройке. Причина, по которой код BeforePost выполняется, конечно, в том, что когда 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 Extract

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
...