我正在将应用程序从BDE转换为ADO。
在BDE下,如果查询是Open并且您调用了“Sql.Clear”,它将自动关闭数据集。
然而,在TADOQuery下并非如此,它会引发异常“无法对已关闭的数据集执行操作”。
我们的许多遗留代码依赖于旧的BDE行为,因此我从代码中获得了大量运行时错误,如下例所示。
我想覆盖我的TADOCustomQuery类的Sql.Clear方法,以便它包含一个“.Close”命令。我怎么能这样做?
“。Clear”方法位于SQL属性上,类型为TWideStrings。我真正的问题是:如何在TADOQuery的后代上覆盖TWideStrings.Clear方法?
我已经有一个自定义的TADOQuery组件,其中包含SQL属性:
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;
答案 0 :(得分:6)
这不是BDE本身的特征。如果你看一下Delphi附带的源码,你会发现你描述的行为是在TQuery.SQL的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;
虽然TADOQuery的SetQuery很简单:
procedure TADOQuery.SetSQL(const Value: TWideStrings);
begin
FSQL.Assign(Value);
end;
为什么Borland / Codegear决定不实施它同样超出我的范围。在自定义ADOQuery中实现TQuery的SetQuery应该可以为您提供所需的行为。
答案 1 :(得分: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
修改强> 作为替代方法,您可以创建一个名为SQLCLEAR的新表单方法,如下所示:
function TYourFormOrDataModule.SqlClear;
begin
qryAdo.Close;
qryAdo.Sql.Clear;
qryBde.Sql.Clear;
end;
然后执行搜索并将“SQL.Clear”替换为“SqlClear”。但我更喜欢在原始答案中执行关闭的方法,因为它更加一致,并且更容易长期维护。使用像gExperts这样的工具来查找Sql.Clear的所有实例,并在它变得微不足道之前插入qryAdo.Close ......即使有几百个实例。
答案 2 :(得分: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使用。
答案 3 :(得分:2)
感谢 别
答案 4 :(得分: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;
答案 5 :(得分:1)
而不是你,我会这样做:如果行为真的像Don说的那样,请与拥有D2009的人核实 - 例如。送他(或给另一个有D2009的人)一个测试用例。如果D2009中的行为是固定的,则问题很简单。
将ADODB.pas复制到项目目录中。修改文件以获得所需的行为(例如,更改SetSQL方法)。重新编译。它应该工作。当您可以从项目中删除旧的自定义ADODB.pas时,这将为您提供最终升级到D2009的时间。
HTH。