2009-03-09 14 views
1

私はアプリケーションをBDEからADOに変換しています。Delphiでこれを行うにはどうすればいいですか?

BDEでは、クエリが開いていて、 "Sql.Clear"を呼び出した場合、自動的にデータセットが閉じられます。

ただし、TADOQueryでは例外が発生します。「閉じられたデータセットに対して操作を実行できません」。

従来のコードの多くは古いBDEの動作に依存しているため、以下の例のようなコードから多くのランタイムエラーが発生します。

".Close"コマンドが含まれるように、私のTADOCustomQueryクラスのSql.Clearメソッドをオーバーライドする必要があります。どうやってやるの?

".Clear"メソッドは、TWideStrings型のSQLプロパティにあります。私の本当の質問は、TADOQueryの子孫でTWideStrings.Clearメソッドをどのようにオーバーライドできますか?

私はSQLプロパティのこれで、既にカスタマイズさTADOQueryコンポーネントを持っています。ここでは

property SQL: TWideStrings read GetSQL write SetSQL; 

は私がいる問題を実証するためのいくつかのコードです:もちろん

procedure TForm1.btnBDEDemoClick(Sender: TObject); 
var 
    qryBDE: TQuery; 
begin 
    //Both queries complete with no problem 
    qryBDE := TQuery.Create(nil); 
    try 
    with qryBDE do begin 
     DatabaseName := 'Test'; //BDE Alias 
     Sql.Clear; 
     Sql.Add('SELECT SYSDATE AS CURDAT FROM DUAL'); 
     Open; 
     ShowMessage('the current date is: ' + FieldByName('CURDAT').AsString); 

     Sql.Clear; //<<<<<NO ERRORS, WORKS FINE 
     Sql.Add('SELECT SYSDATE-1 AS YESDAT FROM DUAL'); 
     Open; 
     ShowMessage('And yesterday was: ' + FieldByName('YESDAT').AsString); 
    end; //with qryBDE 
    finally 
    FreeAndNil(qryBDE); 
    end; //try-finally 
end; 

procedure TForm1.btnADODemoClick(Sender: TObject); 
const 
    c_ConnString = 'Provider=OraOLEDB.Oracle.1;Password=*;'+ 
    'Persist Security Info=True;User ID=*;Data Source=*'; 
var 
    adoConn: TADOConnection; 
    qryADO: TADOQuery; 
begin 
    //First query completes, but the second one FAILS 
    adoConn := TADOConnection.Create(nil); 
    qryADO := TADOQuery.Create(nil); 
    try 
    adoConn.ConnectionString := c_ConnString; 
    adoConn.Connected := True; 
    with qryADO do begin 
     Connection := adoConn; 
     Sql.Clear; 
     Sql.Add('SELECT SYSDATE AS CURDAT FROM DUAL'); 
     Open; 
     ShowMessage('the current date is: ' + FieldByName('CURDAT').AsString); 

     Sql.Clear;//<<<<<<<<===========ERROR AT THIS LINE 
     Sql.Add('SELECT SYSDATE-1 AS YESDAT FROM DUAL'); 
     Open; 
     ShowMessage('And yesterday was: ' + FieldByName('YESDAT').AsString); 
    end; //with qryADO 
    finally 
    FreeAndNil(qryADO); 
    FreeAndNil(adoConn); 
    end; //try-finally 
end; 

答えて

5

問題は、あなたが明確に発行したときに、あなたのデータセットが開いていることです。 ADODatasetの場合、プロパティは基になるADOデータセットを更新するために配線されており、データセットを開いたまま変更すると例外が発生します。

あなたがしなければならないことは、クリアする前にデータセットを閉じることです。すべて正常に動作します。

with qryADO do 
    begin  
    Connection := adoConn;  
    Sql.Clear;  
    Sql.Add('SELECT SYSDATE AS CURDAT FROM DUAL');  
    Open;  
    ShowMessage('the current date is: ' + FieldByName('CURDAT').AsString); 
    qryADO.close; // <=== line added to close the database first. 
    Sql.Clear;  
    Sql.Add('SELECT SYSDATE-1 AS YESDAT FROM DUAL');  
    Open;  
    ShowMessage('And yesterday was: ' + FieldByName('YESDAT').AsString);  
    end; //with qryADO 

EDITあなたは、次のようになりますSQLCLEARという名前の新しいフォームメソッド作成することができます別の方法として

function TYourFormOrDataModule.SqlClear; 
begin 
    qryAdo.Close; 
    qryAdo.Sql.Clear; 
    qryBde.Sql.Clear; 
end; 

をしてから検索を行うとする「SQL.Clear」のために交換してください"SqlClear"。しかし、私はより一貫し、長期的に維持するのがはるかに容易になるので、私は元の答えでクローズを実行する方法を好む。 gExpertsのようなツールを使ってSql.Clearのすべてのインスタンスを見つけ、qryAdo.Closeを挿入します。数百インスタンスある場合でもそれは簡単です。

+0

はい、私はそれを知っています...しかし、私はコード内の既存の場所の100sにこの行を追加することを避けることを望んでいます。 – JosephStyons

2

、 Clearメソッドを上書きするTAdoQueryのサブクラスを作成できます。しかし、私はそれが悪い習慣だと思う。

すべてのクエリを変更する方が良いです。それはおそらくいくつかの仕事ですが、最終的にそれは支払う。あなたが助けにTAdoQueryの例を見れば

ADOQuery := TADOQuery.Create(Self); 
ADOQuery.Connection := ADOConn; 
ADOQuery.SQL.Add(SQLStr); 

{ Update the parameter that was parsed from the SQL query: AnId } 
Param := ADOQuery.Parameters.ParamByName('AnId'); 
Param.DataType := ftInteger; 
Param.Value := 1; 

{ Set the query to Prepared - will improve performance } 
ADOQuery.Prepared := true; 

try 
    ADOQuery.Active := True; 
except 
    on e: EADOError do 
    begin 
    MessageDlg('Error while doing query', mtError, 
       [mbOK], 0); 

    Exit; 
    end; 
end; 

あなたはそれがもう少し異なるBDEのバージョンよりも参照してください。

したがって、おそらくExecuteSQL関数(BDEの最初)を作成し、ADOで使用するように書き換えることが最善の方法です。

+0

まあ、私は多分ここでは密度が高いですが、ClearメソッドはTWideStrings型のSQLプロパティにあります。 TADOQueryの子孫に対してTWideStrings.Clearメソッドをオーバーライドするにはどうすればよいですか?おそらくそれは私の本当の質問だったはずでした.... – JosephStyons

+0

申し訳ありません、あなたの質問を誤解。あなたはクリアを上書きすることはできません。しかし、もう一度、エラーメッセージを表示して、何か他のものが間違っているはずはありません。 –

2

どのデルファイのバージョンを使用していますか?私はこれを複製しようとしましたが、delphi 2009ではうまくいきます - エラーは報告されず、私が期待するデータを返します。

おかげ ドン

+0

私は同じ問題を抱えているDelphi 2007とDelphi 5を使用しています。私はD2009に同じ問題がないことは興味深いことがわかります。 – JosephStyons

6

これはBDEそのものの特徴ではありませんでした。 TADOQueryのはsetQueryは単純ですが

procedure TQuery.SetQuery(Value: TStrings); 
begin 
    if SQL.Text <> Value.Text then 
    begin 
    Disconnect; 
    SQL.BeginUpdate; 
    try 
     SQL.Assign(Value); 
    finally 
     SQL.EndUpdate; 
    end; 
    end; 
end; 

procedure TADOQuery.SetSQL(const Value: TWideStrings); 
begin 
    FSQL.Assign(Value); 
end; 

なぜボーランド/あなたは、Delphiに付属はあなたが説明した動作がTQuery.SQLのはsetQueryメソッドに実装されていることがわかりますソースを見ればCodegearはそれを実装しないことを決定しました。カスタムADOQueryでTQueryのSetQueryを実装すると、必要な動作が得られます。

+0

あなたは正しいですし、私もそのコードを見てきました。しかし、私のカスタムADO SetQueryを修正するだけで、TQueryのようにすることはできません(「Disconnect」ではなく「Close」を除いて全く同じコードを使用しました) – JosephStyons

1

あなたの代わりに、私はこのようにします:ドンが言うように行動が実際に固定されている場合、D2009を持っている人に確認してください。彼(またはD2009を持っている別の人)にテストケースを送ってください。 D2009の動作が固定されている場合、問題は簡単です。

プロジェクトディレクトリにADODB.pasをコピーします。目的の動作をするためにファイルを変更します(たとえば、SetSQLメソッドを変更するなど)。再コンパイル。それは動作するはずです。これにより、D2009への最終的なアップグレードに時間を要します。古いADODB.pasをプロジェクトから削除することができます。

HTH。

2

更新

私は、自動的にすべての私たちのソースコードを更新するための小さなユーティリティを書くことでskamradtのソリューションを実装しました。これは、このように働いた:

1 - 再帰的に私たちのプロジェクトフォルダ内のすべての.PASファイルのリストを取得

2 - これらのファイルのすべてに、この手順を適用します。

procedure ApplyChange(filename: string); 
const 
    c_FindThis = 'SQL.CLEAR'; 
var 
    inputFile, outputFile: TStringList; 
    i, postn, offset: integer; 
    newline: string; 
begin 
    inputFile := TStringList.Create; 
    outputFile := TStringList.Create; 
    offset := 0; 
    try 
    inputFile.LoadFromFile(filename); 
    outputFile.Assign(inputFile); //default: they are the same 

    for i := 0 to inputFile.Count - 1 do begin 
     { 
     whenever you find a "Sql.Clear", place a new line before it, 
     which consists of everything up to the "Sql.Clear" (which may 
     just be whitespace), plus the "Close" command. 
     //} 
     postn := Pos(c_FindThis,Uppercase(inputFile[i])); 
     if (0 < postn) then begin 
     newline := Copy(inputFile[i],1,postn-1) + 'Close;'; 
     outputFile.Insert(i+offset,newline); 
     Inc(offset); 
     end; 
    end; 

    //overwrite the existing file with the revised one 
    outputFile.SaveToFile(filename); 
    finally 
    FreeAndNil(inputFile); 
    FreeAndNil(outputFile); 
    end; //try-finally 
end; 
関連する問題