我在下面的代码中获得了关于Refresh的密钥违规。
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;
tt_emp_id;tt_fromdate
TCustomClientDataSet.InternalRefresh
调用FDSBase.AppendData
时会发生错误。当我们使用SQLDirect作为数据库访问层时,此代码有效,但不再使用FireBird。
可能是什么原因?
ADDED 1-12-2017它与UpdateOptions.RequestLive
的{{1}}属性有关如果我将其默认真值切换为false,一切正常。
这一切都很奇怪。为什么RequestLive的默认值为true?
(为什么它的值实际上没有反映在DFM中,而是EnableDelete,EnableInsert,EnableUpdate切换)?
对于想要复制的人来说,这是完整的.pas源:
(它实际上有一个TFDConnection.
和TDataSource
,但这些只是为了显示数据)
TDBGrid
这是完整的.dfm源:
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.
tt_emp的表结构很简单,只有两个记录,其整数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
的值为20652,1024
tt_emp_id
包含不同tt_emp_contract
值的一些记录,包括20652,不包括1024.结构:
tt_emp_id
答案 0 :(得分:4)
TClientDataSet
使用TDataSetProvider
填充它。TFDQuery
。TFDQuery
将UpdateOptions.RequestLive
设置为true
,这会导致其获取元数据,尤其是每个ProviderFlags
的{{1}}。TField
... select
...语句中检索主(第一个)表的唯一标识列,因此无法设置 tt_fromdate 作为&#34;识别&#34;的一部分键。from
时,后端存储会使用此错误的密钥重新检查其存储记录的唯一性,并引发密钥违例异常。引自online help:
TFDQuery , TFDTable , TFDMemTable 和 TFDCommand 会自动检索唯一标识列(mkPrimaryKeyFields)当...包含在FetchOptions.Items中的fiMeta时,SELECT ... FROM ...语句中的main(第一个)表。
...
当FireDAC无法正确确定它们时,应用程序可能需要明确指定唯一标识列。
Refresh
组件中将RequestLive
设置为false。将其设置为true的主要目的似乎是将FireDAC设置为automatically generate updating SQL commands,因此如果这是一个只读数据集,则可以将其禁用(如果您打算调用is also needed,请注意RefreshRecord
3}})。TFDQuery
子句中的表顺序,因此 tt_emp_contract 是第一个表,因此使用其主键。from
创建持久字段,并在与{em> tt_fromdate 对应的TFDQuery
的{{1}}中设置pfInKey
。ProviderFlags
TField
设置为 tt_emp_id; tt_fromdate 。他们中的任何一个都必须完成这项工作。