如何在winforms中找到并关闭任何打开的OleDbConnections?

Posted

技术标签:

【中文标题】如何在winforms中找到并关闭任何打开的OleDbConnections?【英文标题】:How to find and close any open OleDbConnections in winforms? 【发布时间】:2016-11-16 16:17:08 【问题描述】:

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

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

这是我的功能(通常有效 - 只是在这 1 种情况下不适用)从我们的 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 应用程序知道所有打开的 oledbconnections。有没有办法告诉 c# 查找并迭代所有打开的连接?当我关闭/退出我的应用程序时 - 噗 - 与 mdb 的打开连接被释放,我可以删除文件。所以 .net 中的某些东西知道连接并知道如何释放它——那么我怎样才能在不退出应用程序的情况下利用相同的逻辑呢?

有什么想法吗?

【问题讨论】:

只需在一处创建所有连接,然后您的应用程序中使用的所有连接都将集中在一处 现在您看到了拥有“全局”连接的危险。使用一次性资源的惯用方式是创建、使用和处置它们。 另一个想法(更好)总是为某些查询创建新连接(OleDbConnection instance)并在完成后立即处理它 也就是说,您似乎知道如何关闭连接,所以我不明白到底是什么问题。什么情况下 is 不起作用? 您能否添加代码说明如何在某些 100% 可重现的场景中使用这些代码。问题显然不在于那个小 sn-p,而在于对象是如何被使用的。 【参考方案1】:

已处置 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 来确保您的连接得到正确处理。

【讨论】:

【参考方案2】:

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

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

【讨论】:

【参考方案3】:

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

明确调用“关闭”ADOX 连接对象 调用“处理”它们 调用 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(db.ActiveConnection); 调用 System.Runtime.InteropServices.Marshal.Marshal.FinalReleaseComObject(db); 将它们设置为 Nothing/null

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

【讨论】:

以上是关于如何在winforms中找到并关闭任何打开的OleDbConnections?的主要内容,如果未能解决你的问题,请参考以下文章

C# winform 关闭窗体后在打开 如何让打开的窗体还是原窗体 且打开的窗体还是原状态

winform时按钮如何关闭IE弹出窗口?

C# WinForm中,如何判断窗口已打开

C# winform 主界面打开并关闭登录界面

.NET中开发WinForm程序,如何通过一个窗口打开另外一个窗口,要求上一窗口关闭

更改标题高度/大小Winform