如何在 winforms 中查找和关闭任何打开的 OleDbConnections?

How to find and close any open OleDbConnections in winforms?

首先:我已经看到很多关于这个主题的堆栈讨论(以及在其他论坛上)。 它不是重复的。我尝试了很多建议都无济于事。

我有一个大而复杂的 c# winforms 应用程序。由于各种原因,它会在不同时间与 Access 数据库建立 OleDB 连接。在某个函数中我们需要MOVE(复制+删除)mdb文件,但是因为它被锁定了所以无法完成。我对 unlock/release mdb 文件尝试了很多不同的方法,有时它会起作用。但在某种100%可重现的场景下,是无法解锁的。我们有 2 个全局 oledb 连接变量,我们在任何地方重复使用,以提高效率,并避免在任何地方都有 1-off 连接。这两个连接变量在我们想要关闭连接时很有用,所以我们可以删除 mdb。

这是我的功能(通常有效 - 只是在这 1 种情况下无效)强制 close/release 来自我们的 winforms 应用程序的 2 个 oledb 连接:

public static void CloseOleDBConnections(bool forceReleaseAll = false) {
    if ( DCGlobals.Connection1 != null )
       DCGlobals.Connection1.Close();

    if ( DCGlobals.Connection2 != null )
       DCGlobals.Connection2.Close();

    if ( forceReleaseAll ) {
       DCGlobals.Connection1.Dispose();
       DCGlobals.Connection2.Dispose();
       OleDbConnection.ReleaseObjectPool();
       GC.Collect(GC.MaxGeneration);
       GC.WaitForPendingFinalizers();
    }
}

我将 true 传递给上面的函数。

小小的帮助:请不要浪费时间说 Access 不好、不可扩展等。我知道它不好而且不可扩展,但这是一个遗留要求,我们现在坚持使用它。谢谢。

另一个想法:当然,我的 winforms 应用程序知道所有打开的 oledb 连接。有没有办法告诉 c# 查找和迭代所有打开的连接?当我 close/exit 我的应用程序时 - 噗 - 与 mdb 的打开连接被释放,我可以删除该文件。所以 .net 中的某些东西知道连接并知道如何释放它 - 那么我如何在不退出应用程序的情况下利用相同的逻辑?

有什么想法吗?

Disposed IDataReaders?

您是否正确禁用了所有 IDataReader 对象?它们可能会阻止连接正常关闭。

跟踪解决方案

无论如何,您至少需要更好地跟踪所有连接。这听起来像一个非常大的项目。您需要绝对确定所有连接都已处理。

1.新建 TrackedOleDbConnection 对象

创建一个继承自OleDbConnection 的TrackedOleDbConnection 对象,但添加了一个名为StillOpen 的静态ConcurrentList。当构造 TrackedOleDbConnection 时,将其添加到列表中,当它被处置(覆盖该函数)时,将其删除。

public class TrackedOleDbConnection: OleDbConnection
{
    public TrackedOleDbConnection() : base()
    {
    }

    public TrackedOleDbConnection(string ConnectionString) : base(ConnectionString)
    {
    }

    //You don't need to create a constructor for every overload of the baseclass, only for overloads your project uses
    ConcurrentList<TrackedOleDbConnection> ActiveConnections = new ConcurrentList<TrackedOleDbConnection>();
    void AddActiveConnection()
    {
        ActiveConnections.Add(this);
    }

    override void Dispose()
    {
        ActiveConnections.RemoveIfExists(this); //Pseudo-function
        GC.SuppressFinalise(this);
    }

    //Destructor, to ensure the ActiveConnection is always removed, if Dispose wasn't called
    ~TrackedOleDbConnection()
    {
        //TODO: You should log when this function runs, so you know you still have missing Dispose calls in your code, and then find and add them.
        Dispose();
    }
}

2。不要再直接引用 OleDbConnection

然后在您的解决方案中执行简单的查找和替换以使用 TrackedOleDbConnection。

最后,在您的 CloseOleDBConnections 函数期间,您可以访问 TrackedOleDbConnection.StillOpen 以查看您是否在某处遇到未跟踪连接的问题。

无论您在哪里发现此类未跟踪的问题,都不要使用单一的中央引用,而是 using 以确保正确处理您的连接。

可能如果您唯一需要的是复制文件,则可能不需要弄乱连接。请看这个:

https://www.raymond.cc/blog/copy-locked-file-in-use-with-hobocopy/

ADOX 很可能没有释放与数据库的连接。确保您:

  • 显式调用 'Close' ADOX 连接对象
  • 打电话给 'Dispose' 他们
  • 调用 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db.ActiveConnection);
  • 呼叫System.Runtime.InteropServices.Marshal.Marshal.FinalReleaseComObject(db);
  • 将它们设置为 Nothing/null

此外,当某些东西在文件句柄上调用关闭时,关闭请求将放入队列中以供内核处理。换句话说,即使关闭一个简单的文件也不会立即发生。为此,您可能必须放入一个限时循环来检查 .LDB 文件是否已删除……尽管这最终需要用户等待。寻求此方法的任何其他替代方法,尽管过去有必要使用其他 formats/connections IME。