SqlCeConnection释放时出现的访问冲突异常问题

7

应用程序/代码说明:

我的应用程序基于C#,使用SQL Server CE,并且在同一代码位置仅出现了两次此异常。这个异常直到这个版本才被引入。这个版本唯一的变化是将.NET Framework更改为4.5.2。

当我对SqlCeConnection进行dispose时,我会收到访问冲突异常,错误信息如下:

尝试读取或写入受保护的内存。这通常表示其他内存已损坏。

该异常不会被.NET的try catch子句拦截 - 它会导致崩溃。

在我的代码中,我使用以下内容运行:

try
{
    var connectionString = string.Format("{0}{1}{2}", "Data Source=", _localDB, ";File Mode=Read Write;Max Database Size=4000;Persist Security Info=False;");
    using (var sqlCeConnection = new SqlCeConnection(connectionString))
    {
        using (var sqlCeCommand = new SqlCeCommand())
        {
            sqlCeCommand.Connection = sqlCeConnection;
            sqlCeCommand.CommandText = "SELECT * FROM Application";
            sqlCeConnection.Open();
            var result = (string)sqlCeCommand.ExecuteScalar();
            isValid = !IsValid(result);
        }
    }
}
catch (Exception ex)
{
    _log.Error("exception", ex);
}

第一次崩溃的调用栈:

ntdll!ZwWaitForMultipleObjects+a 
KERNELBASE!WaitForMultipleObjectsEx+e8 
kernel32!WaitForMultipleObjectsExImplementation+b3 
kernel32!WerpReportFaultInternal+215 
kernel32!WerpReportFault+77 
kernel32!BasepReportFault+1f 
kernel32!UnhandledExceptionFilter+1fc 
ntdll! ?? ::FNODOBFM::`string'+2365 
ntdll!_C_specific_handler+8c 
ntdll!RtlpExecuteHandlerForException+d 
ntdll!RtlDispatchException+45a 
ntdll!KiUserExceptionDispatcher+2e 
sqlcese35!__SafeRelease+c 
sqlcese35!Column::`vector deleting destructor'+5c 
sqlcese35!Object::DeleteObjects+39 
sqlcese35!Table::`vector deleting destructor'+45 
sqlcese35!Table::Release+27 
sqlcese35!HashTable::~HashTable+2a 
sqlcese35!Store::~Store+12b 
sqlcese35!Store::Release+2a 
sqlceme35!ME_SafeRelease+17 
DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr ByRef)+78 
[[InlinedCallFrame] (System.Data.SqlServerCe.NativeMethods.SafeRelease)] System.Data.SqlServerCe.NativeMethods.SafeRelease(IntPtrByRef) 
System.Data.SqlServerCe.SqlCeConnection.ReleaseNativeInterfaces()+147 
System.Data.SqlServerCe.SqlCeConnection.Dispose(Boolean)+f1 
System_ni!System.ComponentModel.Component.Dispose()+18 

第二次崩溃的调用栈:

ntdll!NtWaitForMultipleObjects+a 
KERNELBASE!WaitForMultipleObjectsEx+e8 
kernel32!WaitForMultipleObjectsExImplementation+b3 
kernel32!WerpReportFaultInternal+215 
kernel32!WerpReportFault+77 
kernel32!BasepReportFault+1f 
kernel32!UnhandledExceptionFilter+1fc 
ntdll! ?? ::FNODOBFM::`string'+2335 
ntdll!_C_specific_handler+8c 
ntdll!RtlpExecuteHandlerForException+d 
ntdll!RtlDispatchException+45a 
ntdll!KiUserExceptionDispatcher+2e 
<Unloaded_sqlcese35.dll>+7c88c 
<Unloaded_sqlceqp35.dll>+102790 
0x06ccc898 
0x06f9efc8 
0x1eca8018 
0x1f207400 
<Unloaded_sqlcese35.dll>+228dc 
0x00000004 
0x2edff008 
0x00000002 
0x00000003 
0x00000004 
<Unloaded_sqlcese35.dll>+3fbd9 
0x06ccc898 
DomainBoundILStubClass.IL_STUB_PInvoke(IntPtr ByRef)+78 
[[InlinedCallFrame] (System.Data.SqlServerCe.NativeMethods.SafeRelease)] System.Data.SqlServerCe.NativeMethods.SafeRelease(IntPtrByRef) 
System.Data.SqlServerCe.SqlCeConnection.ReleaseNativeInterfaces()+147 
System.Data.SqlServerCe.SqlCeConnection.Dispose(Boolean)+f1 
System_ni!System.ComponentModel.Component.Dispose()+1b 

我在互联网上找到了一些参考资料,建议采取以下措施解决问题:
  1. 可能的解决方案:检查同一连接的多线程问题 (attempted to read write protected memory. this is often an indication that other memory is corrupt)

    拒绝:
    a. 连接是在using语句块中创建的,不会被重复使用。
    b. 调用方法每5分钟执行一次,并通过转储文件验证它没有同时被调用。

  2. 可能的解决方案:sql ce版本不匹配 (http://blogs.msdn.com/b/sqlservercompact/archive/2009/05/06/troubleshooting-access-violation-exception-while-using-sql-server-compact-database-with-ado-net-provider.aspx)

    可能的拒绝:我可以看到安装的版本是3.5 SP2(3.5.8080.0),从dump中定位的模块中可以看到 sqlceme35.dllSystem.Data.SqlServerCe.dll 的版本是3.05.8080.0。

  3. 可能的解决方案,参考以下链接:https://dev59.com/JHRB5IYBdhLWcg3wiHpl#20492181

    可能的拒绝:从统计角度来看,代码在同一位置崩溃了两次,但应用程序中还有另一个写入和读取不同数据库的地方,并且应用程序在那里没有崩溃。

  4. 我最后想到的一点是可能存在DLL卸载问题(请查看第二个调用堆栈)。我猜测dll在应用程序需要它们进行dispose时被从应用程序中卸载,但这似乎有些模糊,是一种‘长期射击’

我的问题是:可能是什么原因导致问题出现,以及可能的解决方案是什么?

你是否尝试共享你的SQL CE数据库?例如,多个用户(进程)在共享位置上访问数据库? - CodeTherapist
1
为什么要将“SELECT *”与ExecuteScalar结合使用 - 请只选择单个值。我建议尝试最新的SQL CE 3.5 SP2版本 - http://erikej.blogspot.dk/2010/08/sql-server-compact-35-sp2-downloadable.html - ErikEJ
嗨,Erik,抱歉我在将代码粘贴到这里时更改了一个列名(法律问题预防)。 - Idan
Erik,还有一个问题-如果SQL CE存在一个错误,在理论上已经在SQL CE 3.5 SP2中修复了,为什么这个方法会重现这个问题?无论如何,在这个版本的更改中都没有提到具体的访问冲突问题,这意味着该问题将“可能被修复”,直到再次重现该问题,这非常难以做到(非确定性验证)。 - Idan
1
您无法捕获AccessViolationException或任何其他已损坏的状态异常。这表明出现了严重问题,而且内存已经被破坏。请参见此答案以获取解决方法。(解决方法仅应用于调试,而非生产代码。) - theB
显示剩余4条评论
1个回答

2

虽然此解决方案尚未得到验证,但解决方案如下:

从第二个调用堆栈中可以看到原生DLL的卸载,我的猜测是SQL连接的dispose方法正在使用其中一种它当前处置的方法。 通过进程转储我验证了所有SqlCeConnection类型都在处理过程中处置。

看到ErikEj的评论让我意识到,如果我查看SQL-CE 3.5到4.0(System.Data.SqlServerCe.dll)之间所做的代码差异,将会更好。

查看代码后,我发现释放方法被移动到dispose方法的一个更晚的位置。

另外,我还可以看到,在调用SafeRelease之前,还有另一个检查,检查是否已释放了安全释放所需的原生DLL,并抛出异常。

总之,SQL-CE 4.0对于相同问题有两个解决方案。

我猜想这个问题是由此引起的。

暂时的解决方案是保持连接在整个应用程序生命周期内(没有连接字符串),这会导致指针池将原生的Dll保留在内存中,直到应用程序生命周期结束。

更好的解决方案是迁移到SQL-CE 4.0。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接