Как добиться самостоятельного клонирования TADODataSet?

Сценарии таковы:

У нас есть некоторые таблицы SQL. Мы выполняем SQL-запрос к этой таблице, и у нас есть результаты в объекте TADOQuery.

var
  qryOryginal, qryClone: TADOQuery;

begin
  //setup all the things here
  qryOryginal.Active := True;
  qryClone.Clone(qryOryginal, ltBatchOptimistic);
  qryOryginal.Delete; //delete in qryOryginal casues that qryClone deletes its record too!
end;

Итак, после клонирования DataSet мой qryClone должен хранить и независимые данные (по крайней мере, я так думал). Однако выполнение Delete для qryOryginal вызывает ту же операцию для qryClone. Я не хочу этого

Есть идеи?

Я знаю, что могу хранить данные в другом месте, возможно, в TClientDataSet, но сначала я хотел бы попробовать вышеуказанное решение.

Спасибо заранее за ваше время.

3 ответа

Решение

Клонирование просто клонирует курсор на наборе данных, не дублируя данные, хранящиеся в наборе данных.

Если вам нужно иметь два независимых данных, вам необходимо скопировать данные из исходного набора данных во второй.

Если вы хотите прочитать или изменить один набор данных без изменения текущего курсора на наборе данных, то вы можете использовать метод Clone.

Вы можете использовать набор записей TADODataSet для клонирования TADODataSet.

ds1.Recordset := CloneRecordset(ds2.Recordset);

Эта версия работает от Delphi XE. ADOInt обновлен определениями библиотеки типов для MDAC 2.8

uses ADOInt, Variants;

function CloneRecordset(const Data: _Recordset): _Recordset;

implementation    

function CloneRecordset(const Data: _Recordset): _Recordset;
var
    newRec: _Recordset;
    stm: Stream;
begin
    newRec := CoRecordset.Create as _Recordset;
    stm := CoStream.Create;
    Data.Save(stm, adPersistADTG);
    newRec.Open(stm, EmptyParam, CursorTypeEnum(adOpenUnspecified),
        LockTypeEnum(adLockUnspecified), 0);
    Result := newRec;
end;

Эта версия должна использоваться для версий Delphi до Delphi XE. ADOR_TLB генерируется из msado28.tlb.

uses ADOInt, ADOR_TLB, Variants;

function CloneRecordset(const Data: ADOInt._Recordset): ADOInt._Recordset;

implementation

function CloneRecordset(const Data: ADOInt._Recordset): ADOInt._Recordset;
var
    newRec: ADOR_TLB._Recordset;
    stm: Stream;
begin
    newRec := ADOR_TLB.CoRecordset.Create as ADOR_TLB._Recordset;
    stm := CoStream.Create;
    (Data as ADOR_TLB._Recordset).Save(stm, adPersistADTG);
    newRec.Open(stm, EmptyParam, CursorTypeEnum(adOpenUnspecified),
        LockTypeEnum(adLockUnspecified), 0);
    Result := newRec as ADOInt._Recordset;
end;

Мне больше нравится реализация с TClientDataSet, потому что мы можем свободно редактировать его по мере необходимости после копирования.

var
  MyADOStoredProc: TADOStoredProc;
  DataSetProvider: TDataSetProvider;
  ClientDataSet: TClientDataSet;
  DataSource: TDataSource;


    ...

    // here now we have an opened ADOStoredProc object MyADOStoredProc
    // let's copy data from it

    DataSetProvider := TDataSetProvider.Create(Self);
    DataSetProvider.Name := 'DataSetProvider' + FormatDateTime('_yyyy_mm_dd_hh_nn_ss', Now);
    DataSetProvider.DataSet := MyADOStoredProc;
    ClientDataSet := TClientDataSet.Create(Self);
    ClientDataSet.ProviderName := DataSetProvider.Name;
    DataSource := TDataSource.Create(Self);
    DataSource.DataSet := ClientDataSet;

    ClientDataSet.Open;
    MyADOStoredProc.Close;

    ClientDataSet.First;
    // here we can modify our ClientDataSet as we need, besides MyADOStoredProc is closed
    if not ClientDataSet.Eof then
      ClientDataSet.Delete;

    ...

Или второй способ, используйте Devexpress dxMemData. Очень полезный и простой в использовании компонент.

var
  MD: TdxMemData;
  SP: TADOStoredProc;
...
...
  // and after opening stored procedure:
  MD.Close;
  MD.Open;
  MD.LoadFromDataset(SP);
Другие вопросы по тегам