如何让 TFDQuery 识别何时将新字段添加到它通过 TFDLocalSQL 查询的 TFDMemTable
How to get TFDQuery to recognise when new fields are added to a TFDMemTable it is querying via TFDLocalSQL
向现有 TFDMemTable 添加字段后,任何通过 TFDLocalSQL 查询 table 的 TFDQuery 都无法识别新字段。
下面的代码说明了这一点:在代码中添加了一个字段的 TFDMemTable 和在 table 上执行 select 的 TFDQuery。 (假设 TFDMemTable 添加到 TFDLocalSQL 的 DataSets 属性 并且 TFDQuery 指向 TFDConnection)将字段添加到实时 TFDMemTable 的过程来自 example project and the use of Unprepare, which doesn't seem to work in this case, comes from this question.
procedure TForm2.FormCreate(Sender: TObject);
begin
with FDMemTable1.FieldDefs.AddFieldDef do begin
Name := 'col1';
DataType := ftString;
Size := 10;
end;
FDMemTable1.CreateDataSet;
FDMemTable1.Active := true;
FDQuery1.SQL.Text := 'SELECT * FROM FDMemTable1';
FDConnection1.Connected := true;
FDLocalSQL1.Active := true;
FDQuery1.Active := true;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
tempMT: TFDMemTable;
begin
tempMT := TFDMemTable.Create(nil);
try
tempMT.Data := FDMemTable1.Data;
FDMemTable1.Close;
with FDMemTable1.FieldDefs.AddFieldDef do begin
Name := 'col99';
DataType := ftString;
Size := 10;
end;
FDMemTable1.Open;
FDMemTable1.MergeDataSet(tempMT, dmDataSet, mmNone);
if not FDMemTable1.FieldDefs.Updated then FDMemTable1.FieldDefs.Update;
finally
tempMT.Free;
FDQuery1.Active := false;
FDQuery1.Unprepare; // this is meant to uncache the fields
FDQuery1.Active := true; // Does not include 'col99' in result set
end;
end;
该字段肯定已添加到 TFDMemTable 并且工作正常。关于如何让 TFDQuery 识别新列的任何提示。
我认为您出现此行为的原因是您的代码缺少一个步骤。
在点击处理程序的 finally
子句中,您需要像这样关闭 FDConnection
finally
tempMT.Free;
FDConnection1.Connected := False;
FDQuery1.Active := false;
FDQuery1.Unprepare; // this is meant to uncache the fields
FDQuery1.Active := true; // Does include 'col99' in result set
Caption := IntToStr(FDQuery1.FieldCount);
end;
PageControl1.ActivePage := TabSheet3; // has FDQuery grid
完成上述更改后,您应该会发现在 FDQuery1 中添加了列。顺便说一句,你可以去掉 FDQuery.Unprepare
,因为它没有区别。 Prepare
通常用于编译后端DB服务器内部查询的SQL代码。在这种情况下,不涉及后端服务器,因为 FDLocalSQL1 组件从内存 table 为 FDQuery1 生成数据。 Unprepare
通常是告诉后台服务器可以释放编译好的查询资源
我认为您的版本不起作用的原因是调用了
GetActualActive
在
procedure TFDCustomLocalSQL.CheckActivate;
var
i: Integer;
begin
if GetActualActive and not FActivated then begin
for i := 0 to DataSets.Count - 1 do
DataSets.CheckUnique(DataSets[i]);
FActivated := True;
InternalAttachToSQL;
for i := 0 to DataSets.Count - 1 do
if DataSets[i].IsValid then
InternalDataSetAdded(DataSets[i]);
end;
end;
GetActualActive
定义为
function TFDCustomLocalSQL.GetActualActive: Boolean;
begin
Result := Active and (Connection <> nil) and Connection.Connected;
end;
因此,如果 FDLocalSQL1 之前处于活动状态,并且 FDConnection 已经连接,则跳过 CheckActivate
的其余部分。因此,断开 FDConnection 允许 CheckActivate
的内部执行,因此 FDLocalSQL1 "notices" 对 FDMemTable1.
结构的更改
顺便说一句,我的项目基于此,而不是从头开始编写代码:
D:\D10\Samples\Object Pascal\Database\FireDAC\Samples\Comp Layer\TFDLocalSQL\InMemDB
这避免了对项目设置方式的一定程度的猜测。
向现有 TFDMemTable 添加字段后,任何通过 TFDLocalSQL 查询 table 的 TFDQuery 都无法识别新字段。
下面的代码说明了这一点:在代码中添加了一个字段的 TFDMemTable 和在 table 上执行 select 的 TFDQuery。 (假设 TFDMemTable 添加到 TFDLocalSQL 的 DataSets 属性 并且 TFDQuery 指向 TFDConnection)将字段添加到实时 TFDMemTable 的过程来自 example project and the use of Unprepare, which doesn't seem to work in this case, comes from this question.
procedure TForm2.FormCreate(Sender: TObject);
begin
with FDMemTable1.FieldDefs.AddFieldDef do begin
Name := 'col1';
DataType := ftString;
Size := 10;
end;
FDMemTable1.CreateDataSet;
FDMemTable1.Active := true;
FDQuery1.SQL.Text := 'SELECT * FROM FDMemTable1';
FDConnection1.Connected := true;
FDLocalSQL1.Active := true;
FDQuery1.Active := true;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
tempMT: TFDMemTable;
begin
tempMT := TFDMemTable.Create(nil);
try
tempMT.Data := FDMemTable1.Data;
FDMemTable1.Close;
with FDMemTable1.FieldDefs.AddFieldDef do begin
Name := 'col99';
DataType := ftString;
Size := 10;
end;
FDMemTable1.Open;
FDMemTable1.MergeDataSet(tempMT, dmDataSet, mmNone);
if not FDMemTable1.FieldDefs.Updated then FDMemTable1.FieldDefs.Update;
finally
tempMT.Free;
FDQuery1.Active := false;
FDQuery1.Unprepare; // this is meant to uncache the fields
FDQuery1.Active := true; // Does not include 'col99' in result set
end;
end;
该字段肯定已添加到 TFDMemTable 并且工作正常。关于如何让 TFDQuery 识别新列的任何提示。
我认为您出现此行为的原因是您的代码缺少一个步骤。
在点击处理程序的 finally
子句中,您需要像这样关闭 FDConnection
finally
tempMT.Free;
FDConnection1.Connected := False;
FDQuery1.Active := false;
FDQuery1.Unprepare; // this is meant to uncache the fields
FDQuery1.Active := true; // Does include 'col99' in result set
Caption := IntToStr(FDQuery1.FieldCount);
end;
PageControl1.ActivePage := TabSheet3; // has FDQuery grid
完成上述更改后,您应该会发现在 FDQuery1 中添加了列。顺便说一句,你可以去掉 FDQuery.Unprepare
,因为它没有区别。 Prepare
通常用于编译后端DB服务器内部查询的SQL代码。在这种情况下,不涉及后端服务器,因为 FDLocalSQL1 组件从内存 table 为 FDQuery1 生成数据。 Unprepare
通常是告诉后台服务器可以释放编译好的查询资源
我认为您的版本不起作用的原因是调用了
GetActualActive
在
procedure TFDCustomLocalSQL.CheckActivate;
var
i: Integer;
begin
if GetActualActive and not FActivated then begin
for i := 0 to DataSets.Count - 1 do
DataSets.CheckUnique(DataSets[i]);
FActivated := True;
InternalAttachToSQL;
for i := 0 to DataSets.Count - 1 do
if DataSets[i].IsValid then
InternalDataSetAdded(DataSets[i]);
end;
end;
GetActualActive
定义为
function TFDCustomLocalSQL.GetActualActive: Boolean;
begin
Result := Active and (Connection <> nil) and Connection.Connected;
end;
因此,如果 FDLocalSQL1 之前处于活动状态,并且 FDConnection 已经连接,则跳过 CheckActivate
的其余部分。因此,断开 FDConnection 允许 CheckActivate
的内部执行,因此 FDLocalSQL1 "notices" 对 FDMemTable1.
顺便说一句,我的项目基于此,而不是从头开始编写代码:
D:\D10\Samples\Object Pascal\Database\FireDAC\Samples\Comp Layer\TFDLocalSQL\InMemDB
这避免了对项目设置方式的一定程度的猜测。