Delphi DataSetProvider BeforeUpdateRecord WideMemoField OldValue 缺失
Delphi DataSetProvider BeforeUpdateRecord WideMemoField OldValue missing
正在为我们的数据库应用程序编写一些更改日志,我遇到了以下问题:在我的 DataSetProvider 的 BeforeUpdateRecord 事件中,任何(已修改的)WideMemo 字段的 OldValue 都丢失了。
很明显,它在我 ApplyUpdates 之前就存在于 ClientDataSet 中,所以在创建增量或由 DataSetProvider 解包的某个地方,它被丢弃了。
我怎样才能得到那个值?
以防万一,这是我使用的组件
客户端:
TClient数据集
TDataSource
服务器端:
TIB查询
TDataSetProvider
连接到 Firebird 数据库
Delphi 东京和 Datasnap
干杯!
我想我会post这是一个新答案,作为对我现在理解的新答案
从OP的评论中可以看出他的具体问题。 OP 想要做的是捕获数据集字段的 OldValue
server 端,即 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
不会
ApplyUpdates
在连接DSP的CDS上调用,因为申请的过程
更新绕过正常的 IBQuery 编辑过程。
但是,如果将DSP的ResolveToDataSet
属性设置为True
,IBQuery1BeforePost
执行并正确提取 AValue
字段的 OldValue,这是一个 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 提取
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
Table 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
正在为我们的数据库应用程序编写一些更改日志,我遇到了以下问题:在我的 DataSetProvider 的 BeforeUpdateRecord 事件中,任何(已修改的)WideMemo 字段的 OldValue 都丢失了。
很明显,它在我 ApplyUpdates 之前就存在于 ClientDataSet 中,所以在创建增量或由 DataSetProvider 解包的某个地方,它被丢弃了。
我怎样才能得到那个值?
以防万一,这是我使用的组件
客户端: TClient数据集 TDataSource
服务器端: TIB查询 TDataSetProvider
连接到 Firebird 数据库
Delphi 东京和 Datasnap
干杯!
我想我会post这是一个新答案,作为对我现在理解的新答案
从OP的评论中可以看出他的具体问题。 OP 想要做的是捕获数据集字段的 OldValue
server 端,即 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
不会
ApplyUpdates
在连接DSP的CDS上调用,因为申请的过程
更新绕过正常的 IBQuery 编辑过程。
但是,如果将DSP的ResolveToDataSet
属性设置为True
,IBQuery1BeforePost
执行并正确提取 AValue
字段的 OldValue,这是一个 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 提取
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
Table 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