Delphi ADO:找到bug上的数据集过滤器

Val*_*riy 3 delphi ado filter locate

我有简单的查询,它返回以下行:

Name  Value
Peter   1
Peter   2
Peter   3
John    1
John    2
Run Code Online (Sandbox Code Playgroud)

应用过滤器:

 ADO.Filter := 'Name="John"';
 ADO.Filtered := True; // Now, its only 2 rows in dataset
 ADO.Locate('Value', 2); 
Run Code Online (Sandbox Code Playgroud)

光标应指向"John 2",但它指向"Peter 2"(由滤镜过滤掉).并且定位返回True.

尝试使用Delphi 7,Rad studio XE 6.看来这个错误是在过去的15年里存在的任何解决方案?

kob*_*bik 6

问题TCustomADODataSet.Locate在于它在内部使用Recordset.Clone并尝试在克隆记录集中查找记录,而不将过滤器设置为克隆记录集(请参阅ADODB TCustomADODataSet.LocateRecord).

来自文档中的备注:

原始Recordset的Filter属性(如果有)将不会应用于克隆.设置新Recordset的Filter属性以过滤结果.复制任何现有Filter值的最简单方法是直接分配它,如下所示.rsNew.Filter = rsOriginal.Filter将新创建的克隆的当前记录设置为第一个记录.

我一直在使用我自己的简单Locate函数来过滤ADO DataSet:基本上,存储当前书签,移动到第一个记录并迭代DataSet直到找到匹配.如果找不到匹配项,则恢复上一个书签.
Bellow是一个非常有限的实现,对我有用(很多todo tho的完美解决方案):

class function TData.Locate(DataSet: TDataSet; const KeyFields: string;
  const KeyValues: Variant; Options: TLocateOptions): Boolean;
{ a very simple Locate function - todo: TLocateOptions & multiple KeyFields/KeyValues }
var
  BM: TBookmarkStr;
begin
  Result := False;
  if DataSet.IsEmpty then Exit;
  BM := DataSet.Bookmark;
  DataSet.DisableControls;
  try
    DataSet.First;
    while not DataSet.Eof do
    begin
      if DataSet.FieldByName(KeyFields).Value = KeyValues then
      begin
        Result := True;
        Break;
      end;
      DataSet.Next;
    end;
    if not Result then DataSet.Bookmark := BM;
  finally
    DataSet.EnableControls;
  end;
end;
Run Code Online (Sandbox Code Playgroud)

另一个选项是修补ADODB.pas TCustomADODataSet.LocateRecord并将其设置FLookupCursor.Filter为与当前数据集过滤器匹配.只要将ADODB.pas修补为项目文件夹中的新副本,就可以使用此选项.

另一种选择是使用TCustomADODataSet.Recordset.Find方法(另请参阅:如何使用TADOQuery使用RecordSet.Find?).