gpt4 book ai didi

delphi - 断开连接后恢复处于 dsInsert 状态的 TADOQuery

转载 作者:行者123 更新时间:2023-12-01 19:48:08 29 4
gpt4 key购买 nike

我们使用具有显式连接的 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 是具有设计时字段绑定(bind)的数据模块的一部分,因此我们无法轻松释放损坏的查询并创建新的查询实例。

编辑:当然,断线的模拟不太现实。但是,通过比较生产错误和测试样本的堆栈跟踪,我们发现测试已经足够好了。

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:该问题不容易重现,因为它似乎依赖于数据。这就是我创建控制台测试程序的原因。请运行测试程序两次并更改主 block 中的测试用例。在我的机器上测试的输出如下所示。

控制台测试程序:

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 专业版
  • Windows 8.1 专业版
  • Windows Server 2008 R2 标准版,6.1.7601 SP1 内部版本 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

最佳答案

我不喜欢发布实际上没有回答问题的答案,但是在这种情况下,我认为我应该这样做,因为我根本无法重现你在您对 quTest 状态的评论。也许是分歧我的结果和你的结果之间是由于你的代码或对象属性的某些部分造成的您的问题中未包含这些内容。

请尝试这个(我已经在 D7 和西雅图测试过):

启动一个新项目并在表单上放置 TAdoConnection 和 TAdoQuery。仅进行下面 DFM 摘录中所示的属性更改;

设置下面代码摘录中所示的事件处理程序。

BeforeCloseBeforeCancel 处理程序中放置断点,其中一个位于

quTest.Post

然后编译、运行并点击Button1。

我得到的结果如下:

  1. BeforeClose 行程中的 BP。

  2. BeforeCancel 行程中的 BP。

  3. quTest.Post 行程上的 BP。

在第 3 步,quTest 的状态为 dsInactive,其 Active 属性为 False。这些值以及预先调用 Before ... 事件的事实是正是我所期望的,因为调用 AdoConnection.Close 关闭使用它作为连接的数据集。

所以,我认为如果你的应用程序得到不同的结果,你需要解释一下为什么因为我认为我已经证明测试项目没有表现出您举报的行为。

更新2

  1. 根据OP的要求,我在表中添加了一个int列'a',并将相应的Parameter添加到quTest并添加

    quTest.Parameters.ParamByName('a').Value:= 0;

在我两次调用quTest.Open之前。当 quTest.Post 上的 BP 跳闸时,这对 quTest 的 State 和 Active 属性没有区别:它们仍然分别为 dsInactive 和 False。

  • 正如OP所说,他只是希望能够在中止插入后继续使用quTest,我将 except block 中的 quTest.Post; 替换为 quTest.Open;。之后,一旦发生插入异常,我可以继续使用 quTest 而不会出现任何明显问题 - 我可以手动执行删除、插入和编辑,并且这些会正确传递回服务器,这样当重新运行应用程序,这些更改仍然存在。
  • 更新 3。 OP 似乎对调用 AdoConnection1.Close 导致 quTest 被关闭表示怀疑。它确实。要验证这一点,请监视 Form1.quTest.RecordSetState 并运行 appl 直至 AdoConnection1.Close。然后,跟踪该调用。你会发现TCustomConnection.SetConnected调用了DoDisconnect它调用 ConnectionObject.Close。这将 quTest.RecordSetState 设置为 stClosed,以便当 TAdoConnection.Disconnect 执行时

    for I := 0 to DataSetCount - 1 do
    with DataSets[I] do
    if stClosed in RecordsetState then Close;

    quTest 已关闭。

    示例代码

      TForm1 = class(TForm)
    ADOConnection1: TADOConnection;
    quTest: TADOQuery;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure quTestBeforeCancel(DataSet: TDataSet);
    procedure quTestBeforeClose(DataSet: TDataSet);
    public
    { Public declarations }
    procedure TestReconnect;
    end;

    [...]

    procedure TForm1.FormCreate(Sender: TObject);
    begin
    quTest.Open;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    begin
    TestReconnect;
    end;

    procedure TForm1.quTestBeforeCancel(DataSet: TDataSet);
    begin
    Caption := 'Before Cancel';
    end;

    procedure TForm1.quTestBeforeClose(DataSet: TDataSet);
    begin
    Caption := 'Before close';
    end;

    procedure TForm1.TestReconnect;
    begin
    quTest.Connection:= ADOConnection1;
    quTest.Open;
    quTest.Insert;
    //quTest.FieldByName('Name').AsString := 'yyyy'; added by MA

    //Simulate lost connection
    ADOConnection1.Close;

    try
    quTest.Post; //Throws 'Operation is not allowed when the object is closed'
    except
    //Reconnect (simplified algorithm)
    ADOConnection1.Connected:= true;
    quTest.Post;
    end;
    end;

    end.

    部分DFM

    object ADOConnection1: TADOConnection
    Connected = True
    ConnectionString =
    'Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initia' +
    'l Catalog=MATest;Data Source=MAI7'
    Provider = 'SQLOLEDB.1'
    Left = 24
    Top = 24
    end
    object quTest: TADOQuery
    Connection = ADOConnection1
    CursorType = ctStatic
    BeforeClose = quTestBeforeClose
    BeforeCancel = quTestBeforeCancel
    Parameters = <>
    SQL.Strings = (
    'Select * from TestTable')
    Left = 64
    Top = 24
    end

    更新 1 以下代码允许完成挂起的插入在 except block 中。请注意 except block 中缺少quTest.Post 的调用。

    procedure TForm1.TestReconnect;
    const
    SaveFileName = 'C:\Temp\testdata.xml';
    begin
    quTest.Connection:= ADOConnection1;
    quTest.Open;
    quTest.Insert;
    quTest.FieldByName('Name').AsString := 'yyyy';
    quTest.SaveToFile(SaveFileName, pfXML);
    //Simulate lost connection
    ADOConnection1.Close;

    try
    quTest.Post; //Throws 'Operation is not allowed when the object is closed'
    except
    //Reconnect (simplified algorithm)
    ADOConnection1.Connected:= true;
    quTest.LoadFromFile(SaveFileName);
    end;
    end;

    关于delphi - 断开连接后恢复处于 dsInsert 状态的 TADOQuery,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38499796/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com