【发布时间】:2018-05-06 03:50:54
【问题描述】:
在下面的代码中,我在刷新时遇到了密钥冲突。
EmployeeContracts 是一个TClientDataSet,通过TDataSetProvider 耦合到TFDQuery,使用SQL:
select ec.*
from tt_emp e, tt_emp_contract ec
where (coalesce(e.tt_nonactive,0)=0)
and e.tt_emp_id = ec.tt_emp_id
代码片段:
with EmployeeContracts do
begin
// Retrieve contracts of all active employees
if (not Active) then
begin
Open;
end;
// Is record already correctly positioned?
if (FieldByName(SEmpID).Asinteger=AEmpID) and
(FieldByName(SFromDate).AsDateTime<=APeilDatum) and
(FieldByName(SToDate).AsDateTime>=APeilDatum) then
begin
Result := True;
Exit;
end;
if not FindKey([AEmpID]) then // Make sure the data are up to date. Refresh from the server.
begin
Refresh; // ERROR HERE
end;
if FindKey([AEmpID]) then
begin
while (FieldByName(SempID).Asinteger=AEmpID) and (not EOF) do
begin
if (FieldByName(SFromDate).AsDateTime<=APeilDatum) and
(FieldByName(SToDate).AsDateTime>=APeilDatum) then
begin
Result := True;
Exit;
end;
Next;
end;
end;
end;
- IndexFieldNames 是
tt_emp_id;tt_fromdate - 我们前面已经过套路了,clientdataset是打开的;只要 FindKey 返回 true 就没有错误
- FetchOnDemand=true,但切换它没有区别
- Delphi Tokyo Win32、FireBird 2.5.3、Dialect 3 数据库(实际上是一个 GDB 文件)
2017 年 11 月 30 日添加:我现在也在同一个应用程序中的 MSSQL 数据库上得到了这个。 - 如果我跟踪 Delphi 代码,在最后调用
FDSBase.AppendData时会在TCustomClientDataSet.InternalRefresh中发生错误。
此代码在我们使用 SQLDirect 作为数据库访问层时有效,但不再适用于 FireBird。
可能是什么原因?
添加 1-12-2017 它与 TFDConnection. 的 UpdateOptions.RequestLive 属性有关如果我将其默认 true 值切换为 false,一切正常。
这一切都很奇怪。为什么 RequestLive 的默认值为 true?
(为什么它的值实际上并没有反映在 DFM 中,而是 EnableDelete、EnableInsert、EnableUpdate 切换了)?。
对于想要复制的人,这是完整的 .pas 源:
(它实际上有 TDataSource 和 TDBGrid 但这些只是为了显示数据)
unit uClientDatasetRefresh;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option,
FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
FireDAC.DApt.Intf, FireDAC.Stan.Async, FireDAC.DApt, FireDAC.UI.Intf,
FireDAC.Stan.Def, FireDAC.Stan.Pool, FireDAC.Phys, FireDAC.Phys.FB,
FireDAC.Phys.FBDef, FireDAC.VCLUI.Wait, Data.DB, Vcl.StdCtrls, Vcl.Grids,
Vcl.DBGrids, Vcl.ExtCtrls, FireDAC.Comp.Client, FireDAC.Comp.DataSet,
Datasnap.Provider, Datasnap.DBClient;
type
TFrmClientDatasetRefresh = class(TForm)
ClientDataSet1: TClientDataSet;
DataSetProvider1: TDataSetProvider;
FDQuery1: TFDQuery;
FDConnection1: TFDConnection;
Panel1: TPanel;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
function PositionOnEmployeeContractRecord(AEmpID: integer; ADate: TDateTime = 0): Boolean;
public
end;
var
FrmClientDatasetRefresh: TFrmClientDatasetRefresh;
implementation
{$R *.dfm}
procedure TFrmClientDatasetRefresh.Button1Click(Sender: TObject);
begin
PositionOnEmployeeContractRecord(20652); // Has records in tt_emp_contract
PositionOnEmployeeContractRecord(1024); // Has no records in tt_emp_contract
end;
const
SEmpID = 'tt_emp_id';
SFromDate = 'tt_fromdate';
SToDate = 'tt_todate';
function TFrmClientDatasetRefresh.PositionOnEmployeeContractRecord(AEmpID: integer; ADate: TDateTime = 0): Boolean;
begin
Result := False;
if (AEmpID=0) then Exit;
if ADate=0 then ADate := Date;
with ClientDataSet1 do
begin
if (not Active) then
begin
Open;
end;
if (FieldByName(SEmpID).Asinteger=AEmpID) and
(FieldByName(SFromDate).AsDateTime<=ADate) and
(FieldByName(SToDate).AsDateTime>=ADate) then
begin
Result := True;
Exit;
end;
if not FindKey([AEmpID]) then
begin
Refresh;
end;
if FindKey([AEmpID]) then
begin
while (FieldByName(SempID).Asinteger=AEmpID) and (not EOF) do
begin
if (FieldByName(SFromDate).AsDateTime<=ADate) and
(FieldByName(SToDate).AsDateTime>=ADate) then
begin
Result := True;
Exit;
end;
Next;
end;
end;
end;
end;
end.
这是完整的 .dfm 源代码:
object FrmClientDatasetRefresh: TFrmClientDatasetRefresh
Left = 0
Top = 0
Caption = 'ClientDataset Refresh'
ClientHeight = 276
ClientWidth = 560
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
PixelsPerInch = 96
TextHeight = 13
object Panel1: TPanel
Left = 0
Top = 0
Width = 560
Height = 41
Align = alTop
BevelOuter = bvNone
TabOrder = 0
ExplicitLeft = 16
ExplicitTop = 8
ExplicitWidth = 185
object Button1: TButton
Left = 32
Top = 8
Width = 75
Height = 25
Caption = 'Test'
TabOrder = 0
OnClick = Button1Click
end
end
object DBGrid1: TDBGrid
Left = 0
Top = 41
Width = 560
Height = 235
Align = alClient
DataSource = DataSource1
TabOrder = 1
TitleFont.Charset = DEFAULT_CHARSET
TitleFont.Color = clWindowText
TitleFont.Height = -11
TitleFont.Name = 'Tahoma'
TitleFont.Style = []
end
object ClientDataSet1: TClientDataSet
Aggregates = <>
IndexFieldNames = 'tt_emp_id;tt_fromdate'
Params = <>
ProviderName = 'DataSetProvider1'
Left = 288
Top = 8
end
object DataSetProvider1: TDataSetProvider
DataSet = FDQuery1
Left = 376
Top = 8
end
object FDQuery1: TFDQuery
Connection = FDConnection1
SQL.Strings = (
'select ec.*'
'from tt_emp e, tt_emp_contract ec'
'where (coalesce(e.tt_nonactive,0)=0)'
'and e.tt_emp_id = ec.tt_emp_id')
Left = 448
Top = 8
end
object FDConnection1: TFDConnection
Params.Strings = (
'DriverID=FB'
'Database=*****.GDB'
'Password=masterkey'
'User_Name=SYSDBA')
LoginPrompt = False
Left = 528
Top = 8
end
object DataSource1: TDataSource
DataSet = ClientDataSet1
Left = 216
Top = 8
end
end
tt_emp 的表结构很简单,只有两条记录,整数为tt_emp_id,值为20652、1024tt_emp_contract 有一些记录为不同的tt_emp_id 值,包括20652,不包括1024。结构:
TT_EMP_ID Integer
TT_FROMDATE DateTime
TT_TODATE DateTime
TT_HOURS Float
... more
Index TT_I0_EMP_CONTRACT on TT_EMP_ID, TT_FROMDATE Primary, Unique
【问题讨论】:
-
如果您不提供
IndexFieldNames,您能检查一下会发生什么吗?也许 ClientDataSet 在重新附加之前无法清除? -
@nil 它需要IndexFieldNames,否则会报错No index current active。根据您的建议,我在 Refresh 之前尝试了 EmptyDataSet 但这没有帮助。将 Refresh 替换为 Close;打开有帮助,但我担心可能带来的开销。
-
几年前,我在刷新通过
TDataSetProvider连接到 dbExpress 数据集的TClientDataSet时遇到了一些奇怪的问题。其中一些在提供程序选项中将poRetainServerOrder固定设置为true,尽管我最终放弃并最终做了Close+Open。 -
@JRL
poRetainServerOrder对我没有帮助。谢谢。
标签: delphi firedac tclientdataset delphi-10.2-tokyo