EntityException:基础提供程序在打开时失败.一台服务器能否关闭数据库连接,使另一台服务器在打开时失败



我遇到一个用VB编写的ASP.NET应用程序的数据库连接错误,该应用程序在三台IIS服务器上运行。底层数据库是位于共享网络设备上的MS Access。它使用实体框架、代码优先实现和JetEntityFrameworkProvider。

应用程序运行稳定。但是,大约每1000次打开数据库连接的尝试中就有1次失败,原因是以下两个错误之一:

06:33:50   DbContext  "Failed to open connection at 2/12/2020 6:33:50 AM +00:00 with error: 
Cannot open database ''.  It may not be a database that your application recognizes, or the file may be corrupt.

14:04:39   DbContext  "Failed to open connection at 2/13/2020 2:04:39 PM +00:00 with error: 
Could not use ''; file already in use.

一秒钟后,通过刷新(F5),错误消失,它再次工作。

有关环境和使用的代码的详细信息。

连接字符串

<add name="DbContext" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source=x:thedatabase.mdb;Jet OLEDB:Database Password=xx;OLE DB Services=-4;" providerName="JetEntityFrameworkProvider" />

DbContext管理

应用程序使用公共属性访问DbContext。DbContext在请求的生存期内保留在HttpContext.Current.Items集合中,并在请求结束时进行处理。

Public Shared ReadOnly Property Instance() As DbContext
Get
SyncLock obj
If Not HttpContext.Current.Items.Contains("DbContext") Then
HttpContext.Current.Items.Item("DbContext") = New DbContext()
End If
Return HttpContext.Current.Items.Item("DbContext")
End SyncLock
End Get
End Property

BasePage初始化并释放DbContext。

Protected Overrides Sub OnInit(e As EventArgs)
MyBase.OnInit(e)
DbContext = Data.DbContext.Instance
...
End Sub
Protected Overrides Sub OnUnload(e As EventArgs)
MyBase.OnUnload(e)
If DbContext IsNot Nothing Then DbContext.Dispose()
End Sub

我尝试过的

SO上解决上述错误消息的许多问题通常涉及无法建立与数据库的连接——他们根本无法连接。这与本案不同。连接工作99,99%的时间。

除此之外,我还检查了:

  • 权限:对.mdb(数据库)和.ldb(锁定文件)所在的共享授予完全访问权限
  • 网络连接:共享设备没有连接问题;这是一个千兆局域网连接
  • 未达到最大255个并发连接数
  • 未超过数据库的最大大小(数据库只有5 MB)
  • 根据这篇MS Dev Net文章的建议,将编译选项从"Any CPU"更改为"x86">

Quote:我收到了同样的"无法打开数据库''"错误,但完全是随机的(看起来)。MDB文件小于1Mb,因此2Gb限制没有问题,正如这个错误中提到的那样。它在32位版本的windows上100%有效,但我发现问题出在64位安装上。该应用程序被编译为"任意CPU"。我将编译选项从"AnyCPU"更改为"x86",问题就消失了。

到目前为止没有任何帮助。

为了收集更多信息,我在DbContext中附加了一个Nlog记录器,它将所有数据库操作和查询写入日志文件。

Shared Log As Logger = LogManager.GetLogger("DbContext")
Me.Database.Log = Sub(s) Log.Debug(s)

通过调查日志,我发现当一台服务器上发生上述错误时,另一台服务器(共3台)同时关闭了数据库连接。这里有两个与上述错误相对应的例子:

06:33:50   DbContext  "Closed connection at 2/12/2020 6:33:50 AM +00:00
14:04:39   DbContext  "Closed connection at 2/13/2020 2:04:39 PM +00:00

假设

当DbContext的所有连接都已关闭时,相应的记录将从.ldb锁文件中删除。当打开到数据库的连接时,将向锁定文件中添加一条记录。当这两个事件在两个不同的服务器上同时发生时,.ldb锁文件会发生写入冲突,从而导致上面的一个错误。

问题

有人能证实或证明这是错误的吗?有人经历过这种行为吗?也许我错过了别的东西。我非常感谢您在这方面的投入和经验。

如果我的假设是真的,那么一个解决方案可能是使用一个助手类来访问数据库,它会捕获并处理这个错误,等待最短的时间,然后重试。

但这感觉有点不对。因此,我也对"适当"解决方案的建议持开放态度。

编辑:"正确"的解决方案是使用DBMS服务器(如下面的注释所述)。我知道这一点。现在,我必须处理这个设计错误而不承担责任。而且,我不能在短期内改变它。

由于空间的原因,我把它写成了一个答案,但这并不是一个真正的答案。

这肯定是一个OleDb提供程序问题
我认为这是一个共享问题。你可以尝试一下:

  • 使用更新的OleDb提供程序,而不是Microsoft.Jet.OLEDB.4.0。(如果你已经尝试了64位,你可能已经尝试了另一个提供商,因为Jet.OLEDB.4.0只有32位)
  • new DbContext()上实现重试机制
  • 阅读你的测试,这可能不是你的情况。我认为Dispose在Jet.OLEDB.4.0连接上并不总是能正常工作。我在测试中注意到了它,并使用不同的测试引擎解决了它。在放弃之前,我使用了这段代码
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);GC.WaitForPendingFinalizers();
    GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
    正如您阅读此代码所理解的,它们是尝试,最新的解决方案是更改测试引擎。

  • 如果你的应用程序不是太忙,你可以尝试使用不同的机制锁定数据库(例如使用锁定文件)。这与new DbContext()重试没有什么不同。

  • 在90年代末,我记得我遇到了一个与磁盘共享操作系统有关的问题(我当时使用的是Novel Netware)。事实上,我没有在网络共享上使用mdb文件的经验。您可以尝试移动与Windows共享的文件夹上的mdb
  • 实际上,我使用Access数据库只是为了测试。如果你真的需要使用单个文件数据库,你可以尝试其他解决方案:SQLLite(你需要一个库,也是我写的,首先应用代码https://www.nuget.org/packages/System.Data.SQLite.EF6.Migrations/)或SQL Server CE
  • 使用DBMS服务器。这无疑是最好的解决方案。作为JetEntityFrameworkProvider的作者,我认为单文件数据库非常适合单用户应用程序(对于此应用程序,我建议使用SQL Lite)、测试(我认为对于测试,JetEntityFrameworkProvider非常好)、传输数据或只读应用程序。在其他情况下,使用DBMS服务器。正如您所知,使用EF,您可以毫不费力地从JetEntityFrameworkProvider更改为SQL Server或MySql

您在设计阶段出错:MS Access数据库引擎不适合ASP.Net网站,这在多个地方都有明确说明,例如详细信息下的官方下载页面。

Access Database Engine 2016 Redistributable不是有意的。。。。由…使用。。。从服务器端web应用程序(如ASP.NET )调用的程序

如果您真的必须使用Access数据库,可以运行一个助手类,在出现常见错误时重试。但我不推荐。

这里的正确解决方案是使用不同的RDBMS,它表现出无状态行为。我推荐SQL Server Express,它有一些限制,但如果超过这些限制,您将远远超出Access支持的范围,并且不会导致这样的错误。

最新更新