【发布时间】:2016-07-21 09:09:03
【问题描述】:
我们使用带有显式插入连接的 Delphi TADOQuery。
总结: 当查询处于状态 dsInsert 时连接丢失时,查询似乎进入了与基础 ADO 记录集相关的不一致状态。因此,即使重新建立了连接,也无法再使用该查询。
详情:
假设以下简化步骤:
quTest.Connection:= ADOConnection1;
quTest.Open;
quTest.Insert;
//Simulate lost connection
ADOConnection1.Close;
try
//quTest.State is still dsInsert
quTest.Post; //Throws 'Operation is not allowed when the object is closed'. This is the expected beavior.
except
//Reconnect (simplified algorithm)
ADOConnection1.Connected:= true;
end;
//quTest.State is still dsInsert
//So far, so good.
//Now let's close or abort or somehow reset quTest so that we can use it again. How?
quTest.Close //throws 'Operation is not allowed when the object is closed'
问题是,在上面的代码示例的末尾,quTest 仍处于状态 dsInsert,但底层 ADO 记录集已断开连接。 任何关闭或以某种方式重置 quTest 的尝试都会失败,并出现异常“对象关闭时不允许操作”。
请注意,我们的目标不是继续初始插入操作。我们只是想让查询回到我们可以打开并再次使用它的状态。
这可能吗?
由于 quTest 是具有设计时字段绑定的数据模块的一部分,因此我们无法轻松释放损坏的查询并创建新的查询实例。
编辑: 当然,断开连接的模拟不太现实。 但是,对比生产错误和测试样本的堆栈跟踪,我们发现测试已经足够好了。
Production stack trace:
================================================================================
Exception class : EOleException
Exception message: Operation is not allowed when the object is closed
EOleException.ErrorCode : -2146824584
================================================================================
[008B9BD7] Data.Win.ADODB.TCustomADODataSet.InternalGotoBookmark + $17
(0000E290) [0040F290]
[008B9BD7] Data.Win.ADODB.TCustomADODataSet.InternalGotoBookmark + $17
[008B9BF4] Data.Win.ADODB.TCustomADODataSet.InternalSetToRecord + $14
[0081EEBE] Data.DB.TDataSet.InternalSetToRecord + $2
[0081D576] Data.DB.TDataSet.SetCurrentRecord + $62
[0081D9A4] Data.DB.TDataSet.UpdateCursorPos + $10
[0081E378] Data.DB.TDataSet.Cancel + $68
[0081AA49] Data.DB.TDataSet.SetActive + $AD
[0081A841] Data.DB.TDataSet.Close + $9
Test case stack trace:
Data.Win.ADODB.TCustomADODataSet.InternalFirst
Data.DB.TDataSet.SetCurrentRecord(0)
Data.DB.TDataSet.UpdateCursorPos
Data.DB.TDataSet.Cancel
Data.DB.TDataSet.SetActive(???)
Data.DB.TDataSet.Close
实际上,由于查询状态仍为 dsInsert,因此尝试 .Cancel,导致后续调用 ADO 记录集失败。
procedure TDataSet.SetActive(Value: Boolean);
begin
...
if State in dsEditModes then Cancel;
...
end;
编辑 2: 这个问题不容易重现,因为它似乎与数据有关。 这就是我创建控制台测试程序的原因。 请运行两次测试程序并在主块中更改测试用例。 我的机器上的测试输出如下所示。
控制台测试程序:
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
Data.DB,
Data.Win.ADODB,
ActiveX;
procedure Setup(aConnection: TADOConnection; aEmpty: Boolean);
var
query: TADOQuery;
begin
query:= TADOQuery.Create(nil);
try
query.Connection:= aConnection;
//Create test table
try
query.SQL.Add('create table test3 (a int)');
query.ExecSQL;
WriteLn('Table created.');
except
on e: Exception do
Writeln(e.Message);
end;
//Clear test table
query.SQL.Clear;
query.SQL.Add('delete test3');
query.ExecSQL;
if not aEmpty then begin
//Create a row
query.SQL.Clear;
query.SQL.Add('insert into test3 values (0)');
query.ExecSQL;
end;
finally
query.Free;
end;
end;
var
con: TADOConnection;
query: TADOQuery;
begin
CoInitialize(nil);
try
con:= TADOConnection.Create(nil);
query:= TADOQuery.Create(nil);
try
con.ConnectionString:= 'Provider=SQLOLEDB.1;Persist Security Info=False;Integrated Security=SSPI;Data Source=10.0.0.11,1433;Initial Catalog=TestDB';
con.Connected:= true;
//Test case 1: With data
Setup(con, false);
//Test case 2: No data
//Setup(con, true);
query.Connection:= con;
query.SQL.Add('select * from test3');
query.Open;
query.Insert;
con.Close;
WriteLn('query.Active: ' + BoolToStr(query.Active));
WriteLn('query.State: ' + IntToStr(Ord(query.State)));
query.Close;
WriteLn('Test ran without exception.');
except
on E: Exception do
Writeln('Exception: ' + E.ClassName, ': ', E.Message);
end;
finally
ReadLn;
query.Free;
con.Free;
end;
end.
测试环境:
- Delphi 10 西雅图版本 23.0.21418.4207
- 控制台测试程序平台:Win32
- Microsoft SQL Server 2008 R2 (SP1) - 10.50.2550.0 (X64)
测试日期:
- IDE 中的 Windows 8.1 Pro
- Windows 8.1 专业版
- Windows Server 2008 R2 Standard,6.1.7601 SP1 Build 7601
- Windows Server 2008 R2 标准版
测试用例 1 的输出:
There is already an object named 'test3' in the database
query.Active: 0
query.State: 0
Test ran without exception.
测试用例 2 的输出:
There is already an object named 'test3' in the database
query.Active: -1
query.State: 3
Exception: EOleException: Operation is not allowed when the object is closed
【问题讨论】:
-
您遇到的问题是因为您尝试模拟断开连接的方式。当您调用 AdoConnection1.Close 时,这将放弃插入,并关闭 quTest。因此,当您调用 quTest.Post 时,这必然会失败(其状态为 dsInactive)。
-
打开查询,仅此而已
-
@MartynA 不。就在 quTest.Post 之前,quTest.State 仍然是 dsInsert。我们预计 quTest.Post 会失败。我们想在之后恢复 quTest。
-
@SirRufo quTest.Open 会抛出“当对象关闭时不允许操作”,就像 quTest.Close 或 quTest.Cancel。
-
我无法复制你所说的内容。