如何让 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

这避免了对项目设置方式的一定程度的猜测。