【问题标题】:Why qry.post executed with asynchronous mode?为什么 qry.post 以异步方式执行?
【发布时间】:2010-06-12 16:04:35
【问题描述】:

最近遇到一个奇怪的问题,代码截图如下:

var
  sqlCommand: string;
  connection: TADOConnection;
  qry: TADOQuery;
begin
  connection := TADOConnection.Create(nil);
  try
    connection.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Test.MDB;Persist Security Info=False';
    connection.Open();
    qry := TADOQuery.Create(nil);
    try
      qry.Connection := connection;
      qry.SQL.Text := 'Select * from aaa';
      qry.Open;

      qry.Append;
      qry.FieldByName('TestField1').AsString := 'test';

      qry.Post;
      beep;
    finally
      qry.Free;
    end;
  finally
    connection.Free;
  end;
end;

首先,新建一个名为test.mdb的access数据库,放到这个测试项目的目录下,我们可以在其中新建一个名为aaa的表,其中只有一个文本类型字段TestField1。

我们在“哔”行设置断点,然后在ide调试模式下午餐测试应用程序,当ide停止在断点行时(qry.post已经执行),此时我们使用microsoft access打开测试.mdb打开表aaa你会发现表aaa没有任何变化,如果你让ide在按f9后继续运行你可以发现一个新记录插入到表aaa中,但是如果你按ctrl+f2终止应用程序在断点处,你会发现 aaa 表没有插入记录,但正常情况下,执行 qry.post 后应该向 aaa 表插入一条新记录。 谁能解释一下这个问题,困扰我很久了。谢谢!!!

顺便说一句,ide是delphi 2010,access mdb文件是windows 7下microsoft access 2007创建的

【问题讨论】:

    标签: database delphi post transactions


    【解决方案1】:

    Access 不会向您显示尚未提交的事务的记录。在您暂停程序时,连接创建的隐式事务尚未提交。还没有尝试过,但我的猜测是在释放查询后将提交隐式事务。因此,如果您在此之后暂停,您应该会在 MS Access 中看到您的记录。


    从 Ryan 那里得到更多信息(见他对自己的回答)后,我做了更多调查。

    拥有主键(自动编号或其他)似乎不会影响行为。

    以自动编号列作为主键的表

      connection.Execute('insert into aaa (TestField1) values (''Test'')');
      connection.Execute('select * from aaa');
      connection.Execute('delete * from aaa');
      beep;
    finally
      connection.Free;
    end;
    

    在“选择”上停止不会显示新记录。 停止“删除”显示新记录。 即使在重复刷新之后,停止“哔”声仍会显示表中的所有记录。 在“connection.Free”上停止显示表中没有更多记录。嗯? 在“删除”和“哔”之间插入“选择”时停止显示表中没有更多记录。

    同一张桌子

      connection.Execute('insert into aaa (TestField1) values (''Test'')');
      beep;
      connection.Execute('delete * from aaa');
      beep;
      beep;
    

    在每个语句上停止表明 Access 在至少执行了另一个语句之前不会收到“命令”。换句话说:“执行”语句之后的哔哔声必须在 Access 处理该语句之前处理(可能需要几次刷新才能显示,第一次刷新并不总是足够的)。如果您在“执行”语句后的第一个哔声中停止,则 Access 中没有发生任何事情,如果您在不执行任何其他语句的情况下重置程序,则不会发生。

    步入连接。执行(使用 debug dcu's on):执行 sql 语句的效果现在可以在 Access 中看到返回蜂鸣声。实际上,它更早可见。例如,单步执行“delete”语句,记录会在 ADODB 代码中的某处标记为#deleted。

    事实上,在单步执行 adodb 代码时,当在 OnExecuteComplete 处理程序中停止时,记录在 Access 中变得可见。不是在“开始”时停止,而是在“如果分配”之后立即停止。这同样适用于删除语句。当在 AdoDb 的 OnExecuteComplete 处理程序中的 if 语句上停止时,该效果在 Access 中可见。

    Ado 确实有一个 ExecuteOption 来异步执行语句。在所有这些过程中它都没有生效(默认情况下不包括在内)。当我们处理进程外 COM 服务器和回调(例如 OnExecuteComplete 处理程序)时,该处理程序在返回到 AdoDb 中 TAdoConnection.Execute 方法中的 ConnectionObject.Execute 语句之后的语句之前执行。

    总而言之,我认为这不是同步或异步执行的问题,而更多的是引用何时释放(我们正在处理 COM 和接口引用计数),或者线程和进程计时问题(在应用程序中,Access 和它们之间),或与它们的组合。

    调试器可能只是在弄混事情而不是澄清它们。看看 D2010 中的单线程调试功能会发生什么会很有趣,但在我所在的地方(现在和接下来的两周)还没有。

    【讨论】:

    • 我希望在qry.post执行之后插入一条新记录,因为qry.post似乎是同步执行的,但根据测试,它是异步执行的,在"行放置两个断点beep”和“qry.free”行,我发现当应用程序在“beep”行暂停时(qry.post已执行)新记录没有插入,但是当应用程序在“qry.free”行暂停时",记录被插入(qry.free没有被执行),为什么qry.post没有同步执行......
    • 可能是您检查得太早或没有刷新 MS Access 中的表/查询?我刚刚将您的代码逐字复制(只是编辑了连接)到一个新的 vcl 表单应用程序上的按钮的 onclick 处理程序,并在哔声上放置了一个断点。启动应用程序,单击按钮,当它到达断点时,移动到 MS Access,其中新创建的具有两个初始记录的表仍处于打开状态。只需按刷新 (F5) 即可显示从 delphi 应用程序新插入的记录(仍然在哔声处暂停)。将 Access 2007 与“Microsoft.ACE.OLEDB.12.0”提供程序一起使用。
    • 没有足够的字符空间来填充我的消息,看下一个答案
    • 我已经得到答案了,你可以看看我自己的答案,我也很感谢你的帮助:)
    【解决方案2】:

    首先,Marjan,谢谢你的回答,我很确定我当时点击了刷新按钮,但仍然没有任何改变...... 经过多次实验,我发现如果我在表字段中插入一个自增id作为主键,就不会出现这种奇怪的行为,虽然我已经这样做了,但还有一个奇怪的行为,我将显示我的代码片段,如下所示:

    procedure TForm9.btn1Click(Sender: TObject);
    var
      sqlCommand: string;
      connection: TADOConnection;
    begin
      connection := TADOConnection.Create(nil);
      try
        connection.ConnectionString := 'Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Test.MDB;Persist Security Info=False';
        connection.Open();
        connection.Execute('insert into aaa (TestField1) values (''Test'')');
        connection.Execute('select * from aaa');
        connection.Execute('delete * from aaa'); // breakpoint 1
        beep;  // breakpoint2
      finally
        connection.Free;
      end;
    end;
    

    在“delete”和“beep”行设置两个断点,当代码停在断点1时,你可以刷新数据库,你会发现记录被插入了,当代码停在断点2时继续运行,你会发现记录还在里面.....如果这个时候你按下ctrl+f2,记录不会被删除......如果connection.execute是一个真正的同步过程,这不应该发生。抱歉这么晚才看你的答案,因为我在端午节……

    【讨论】:

    • 我去看看。同时:希望龙不要抓到你!
    【解决方案3】:

    Marjan,再次感谢您的回复,但我不能接受连接引擎处理的这种行为,今天我在 MSDN 网站上找到了一些有用的东西,请参阅:

    http://msdn.microsoft.com/en-us/library/ms719649(v=VS.85).aspx

    幸运的是我按照文章解决了问题,其实“Jet OLEDB:Implicit Commit Sync”这个属性的默认值是false,根据这个属性的解释,Be false意味着隐式事务会使用异步模式。所以我们可以做的是通过使用如下代码片段将此属性设置为真:

    connection.Properties.Item['Jet OLEDB:Implicit Commit Sync'].Value := true;
    

    顺便说一句,根据那篇文章,这个属性只能使用连接对象的Properties属性来设置,否则如果在连接字符串中设置,就会出错

    【讨论】:

      猜你喜欢
      • 2019-07-04
      • 2013-04-07
      • 2013-09-29
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 1970-01-01
      • 2021-01-19
      • 1970-01-01
      相关资源
      最近更新 更多