为什么 Oledb Connection.Close() 执行时间太长?

Why does Oledb Connection.Close() take far too long to execute?

在开发连接到本地数据库的桌面应用程序期间,我将数据库移动到网络位置,现在每次调用 Connection.Close() 时程序都会挂起 5-15 秒。当数据库存储在本地计算机上时,我很少会看到这个问题,但现在它在网络上,几乎每次我尝试 Close() 时它都会挂起。我对数据库的第一次调用我什至没有查询它,它只是一个测试连接,我打开和关闭以确保用户可以连接,但它仍然挂得太久。

我之前看到过这个问题,但是除了 'try using() {} to have c# clean it up.' 没有人可以提供修复的建议或解决方案,这不会以任何方式影响 Close() 时间。

连接字符串中是否有解决此问题的选项?有谁知道为什么会这样?

我使用的连接字符串是:

CONNECTION_STRING = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=\NEWTORK\Shared\Database\Database.accdb; Persist Security Info=False;"

private void

  Form_Login_Load(object sender, EventArgs e)
  {
    OleDbConnection Connection = new OleDbConnection();
    Connection.ConnectionString = CONNECTION_STRING;
    try
    {
      Console.Write("Connection Opening.....");
      Connection.Open();
      Console.WriteLine("Connection Opened");
      Console.Write("Writing Status Text.....");
      lbl_Status.Text = "Online";
      Console.WriteLine("Written Status Text");
      Console.Write("Connection Closing.....");
      Connection.Close();
      Console.WriteLine("Connection Closed");
    }
    catch (Exception Ex)
    {
      lbl_Status.Text = "Offline";
      lbl_Status.ForeColor = System.Drawing.Color.FromArgb(255, 0, 0);
      MessageBox.Show("Could not connect to Database");
    }
  }

在我的输出 window 中,我立即看到打开消息和写入状态消息,但应用程序在 'Console.Write("Connection Closing.....");' 行之前挂起。 5-15 秒后,关闭消息出现在 window.

应用程序中有许多查询数据库的连接,似乎就在尝试关闭所有这些连接之前挂起。我似乎确实注意到,在不关闭应用程序的情况下重复相同的查询有时会导致重复关闭的关闭时间更快,但它总是在第一次尝试任何查询时挂起。

如果数据库和程序之间有任何挂起的事务,那么 Close() 将回滚它们。它还必须请求连接池并将其从连接池中删除,这在远程驱动器上可能需要更长的时间。这可能是你的问题吗?

Here are the docs on the method.

要解决这个问题,您可以使用 BackgroundWorker 来执行它,如下所示:

var b = new BackgroundWorker();
b.DoWork += CloseDB;
b.RunWorkerCompleted += someMethodAfterClose;
b.RunWorkerAsync();

关闭数据库:

public void CloseDB(object sender, DoWorkEventArgs e) {
    someConnection.Close();
}

通常这可以是服务器设置。如果防火墙 + windows 防御者处于活动状态,那么它会扫描所有 "file" 访问 - 结果打开非常慢,当然还有 close().

尝试运行共享文件夹所在的计算机关闭防火墙和windows防御者。我看到这经常解决大的延迟。当然,使用基于套接字的技术可以消除这个问题(例如:基于服务器)。然而,你有你所拥有的,通常不是你的代码速度,而是 "windows file" 系统,以及网络和防病毒软件的速度是导致这种速度下降的原因。

最终对我有用的是: 当 Connection.Close() 方法的延迟时间变长时,我使用的是 Microsoft Access 数据库引擎 2016 (x64)。出于不相关的原因,我需要卸载 2016 引擎并使用 2010 (x86) 版本。现在我的 Connection.Close() 时间平均约为 40 毫秒,这对我的应用程序来说是完全可以接受的。

这可能会被标记为 "not an answer",但我也有这个问题,而且似乎没有人找到真正的解释,所以与其创建一个多余的问题,不如以下是我的一些症状:

  • 当办公室更新到 Windows 10(从 Windows 7)时开始更频繁地发生。
  • 经常发生但不总是发生。有时 OleDb 连接 close/dispose 非常快,有时会挂起 ~10-15 秒。对于任何给定的连接尝试,任何时候的变化似乎都是随机的,即使使用相同的 Access 数据库、相同的机器和相同的进程也是如此。
  • 位于本地和网络上的数据库都可能发生挂起,但尚未测试一个是否比另一个更频繁。
  • 即使连接未进行任何更改或交易(即仅 SELECT 查询)也会发生。
  • 仅在 OleDb 连接时发生,即用户通过 Access 界面打开和关闭数据库是可以的。

更新

无奈之下,我尝试了在后台线程中关闭连接的建议,以避免在此过程中阻塞主线程。但是当我这样做时,每当以后启动后续连接时,都会出现此错误:

未处理的异常:System.AccessViolationException:试图读取或写入受保护的内存。这通常表示其他内存已损坏

谷歌搜索表明,很多人在处理 Access 数据库连接时似乎都会遇到这种情况,但通常是出于未知原因。

所以我尝试了一个不同的建议,即更改 conn 字符串以包含 OLE DB Services=-1;。起初这似乎解决了问题。没有这方面的专家,据我所知,它基本上通过在后台打开一些连接资源以供重用来避免挂起关闭问题,即使在处理 conn 对象之后也是如此。对我来说很好......除了最终它似乎关闭了那些资源(可能是一些超时),然后当稍后建立连接时,回到上面莫名其妙的AccessViolationException

可能的解决方案

从各种网络评论和我自己的实验拼凑而成:

  • OleDb 不支持多线程。
  • 关闭应用程序中 last 打开的 Access 连接,无论是什么数据库,似乎都会触发 OleDb 库本身内部某些资源的卸载,因此这是一个潜在的时间-消耗操作。

所以我做了什么,添加一个隐藏在我的应用程序中的空 Access 数据库,并在启动时打开一个 OleDb 连接到它。我不处理连接并维护对连接的引用以防止它自行关闭。这样,任何后续连接的处理都不会触发 OleDb 保留的任何资源的卸载。

这是一个 hack,但到目前为止,这似乎解决了原始问题。

当然,真正的答案是避免使用 Access 数据库!