You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

FDQuery设fmOnDemand时Locate全量拉取,求更优雅解决方案

FDQuery fmOnDemand模式下Locate避免拉全量数据的优雅方案

问题根源

当FDQuery.FetchOptions.Mode设为fmOnDemand时,Locate/LocateEx会触发FireDAC自动拉取所有未加载的数据——因为Locate需要全局遍历数据集匹配目标项,而fmOnDemand的按需加载逻辑会被这个全局查找需求打破。切换到fmManual模式后,FireDAC不会自动拉取数据,Locate仅在已加载到本地的数据集中查找,从而避免全量拉取。


方案1:用BeforeLocate/AfterLocate事件自动切换模式

无需修改现有数据集类,通过绑定事件封装模式切换逻辑,同时加入线程同步适配异步场景:

procedure TYourForm.FDQueryBeforeLocate(DataSet: TDataSet; var KeyFields: string; KeyValues: Variant; Options: TLocateOptions; var Accept: Boolean);
var
  LQuery: TFDQuery;
begin
  if DataSet is TFDQuery then
  begin
    LQuery := TFDQuery(DataSet);
    // 用Tag临时保存原模式,加同步防止异步线程冲突
    TMonitor.Enter(LQuery);
    try
      LQuery.Tag := Integer(LQuery.FetchOptions.Mode);
      LQuery.FetchOptions.Mode := fmManual;
    finally
      TMonitor.Exit(LQuery);
    end;
  end;
end;

procedure TYourForm.FDQueryAfterLocate(DataSet: TDataSet; Found: Boolean);
var
  LQuery: TFDQuery;
begin
  if DataSet is TFDQuery then
  begin
    LQuery := TFDQuery(DataSet);
    TMonitor.Enter(LQuery);
    try
      LQuery.FetchOptions.Mode := TFetchMode(LQuery.Tag);
    finally
      TMonitor.Exit(LQuery);
    end;
  end;
end;

优点:无需替换现有FDQuery实例,直接绑定事件即可生效,适合已有项目快速改造。


方案2:重写TFDQuery封装Locate逻辑

如果希望代码更封装,可自定义TFDQuery子类,将模式切换逻辑内置到Locate方法中:

type
  TMyFDQuery = class(TFDQuery)
  public
    function Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): Boolean; override;
    function LocateEx(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; const Filter: string = ''): Boolean; override;
  end;

function TMyFDQuery.Locate(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions): Boolean;
var
  LOriginalMode: TFetchMode;
begin
  // 线程同步保护,适配异步场景
  TMonitor.Enter(Self);
  try
    LOriginalMode := FetchOptions.Mode;
    FetchOptions.Mode := fmManual;
    try
      Result := inherited Locate(KeyFields, KeyValues, Options);
    finally
      FetchOptions.Mode := LOriginalMode;
    end;
  finally
    TMonitor.Exit(Self);
  end;
end;

function TMyFDQuery.LocateEx(const KeyFields: string; const KeyValues: Variant; Options: TLocateOptions; const Filter: string): Boolean;
var
  LOriginalMode: TFetchMode;
begin
  TMonitor.Enter(Self);
  try
    LOriginalMode := FetchOptions.Mode;
    FetchOptions.Mode := fmManual;
    try
      Result := inherited LocateEx(KeyFields, KeyValues, Options, Filter);
    finally
      FetchOptions.Mode := LOriginalMode;
    end;
  finally
    TMonitor.Exit(Self);
  end;
end;

优点:调用方无需关注模式切换逻辑,直接使用自定义类即可,代码更简洁可维护。


异步场景适配注意事项

  1. 线程同步:无论用事件还是重写方法,必须用TMonitor或TCriticalSection保护模式切换逻辑,防止多线程同时修改FetchOptions.Mode导致的状态混乱。
  2. 数据集隔离:异步线程尽量使用独立的FDQuery实例,避免跨线程操作同一个UI线程的数据集,减少同步冲突。
  3. 避免嵌套操作:不要在Locate的事件回调(如AfterScroll)中触发其他数据集操作,防止嵌套同步导致死锁。

内容的提问来源于stack exchange,提问作者BxOO

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:05:09