WCF服务中的SQLite AccessViolationException

12
我们有一个.NET Windows服务,向用户界面和我们系统的其他部分公开了一个WCF服务。它针对.NET Framework 4.5进行了定位,并使用SQLite 1.0.92二进制文件与底层SQLite数据库通信。然而,Windows服务在运行一段时间后会崩溃(自动停止),并在SQLite.Interop.dll中发现AccessViolationException(通过Windows事件查看器)。我已经阅读了一些关于Connection close中这个异常的文章,但在我们所有的情况下,我们在使用我们的WCF服务公开的方法查询或写入数据库时遇到了这个异常。堆栈跟踪如下:
Application: OurServer.exe
Framework Version: **v4.0.30319**
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException
Stack:
   at System.Data.SQLite.UnsafeNativeMethods.sqlite3_bind_int(IntPtr, Int32, Int32)
   at System.Data.SQLite.UnsafeNativeMethods.sqlite3_bind_int(IntPtr, Int32, Int32)
   at System.Data.SQLite.SQLite3.Bind_Int32(System.Data.SQLite.SQLiteStatement, System.Data.SQLite.SQLiteConnectionFlags, Int32, Int32)
   at System.Data.SQLite.SQLiteStatement.BindParameter(Int32, System.Data.SQLite.SQLiteParameter)
   at System.Data.SQLite.SQLiteStatement.BindParameters()
   at System.Data.SQLite.SQLiteCommand.GetStatement(Int32)
   at System.Data.SQLite.SQLiteDataReader.NextResult()
   at System.Data.SQLite.SQLiteDataReader..ctor(System.Data.SQLite.SQLiteCommand, System.Data.CommandBehavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteReader(System.Data.CommandBehavior)
   at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(System.Data.CommandBehavior)
   at DataAccess.Sqlite.ExecuteCommand(System.Collections.ObjectModel.Collection`1<System.String>, System.Collections.ObjectModel.Collection`1<System.Data.Common.DbParameter[]>)
   at Data.Settings.Save(System.Collections.ObjectModel.Collection`1<Common.Operation>)
   at DynamicClass.SyncInvokeSaveOperation(System.Object, System.Object[], System.Object[])
   at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(System.Object, System.Object[], System.Object[] ByRef)
   at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(System.ServiceModel.Dispatcher.MessageRpc ByRef)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(System.ServiceModel.Dispatcher.MessageRpc ByRef)
   at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(System.ServiceModel.Dispatcher.MessageRpc ByRef)
   at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean)
   at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(System.ServiceModel.Channels.RequestContext, Boolean, System.ServiceModel.OperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(System.ServiceModel.Channels.RequestContext, System.ServiceModel.OperationContext)
   at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(System.IAsyncResult)
   at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult)
   at System.Runtime.AsyncResult.Complete(Boolean)
   at System.Runtime.InputQueue`1+AsyncQueueReader[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Set(Item<System.__Canon>)
   at System.Runtime.InputQueue`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].EnqueueAndDispatch(Item<System.__Canon>, Boolean)
   at System.Runtime.InputQueue`1[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].EnqueueAndDispatch(System.__Canon, System.Action, Boolean)
   at System.ServiceModel.Channels.SingletonChannelAcceptor`3[[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.__Canon, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]].Enqueue(System.__Canon, System.Action, Boolean)
   at System.ServiceModel.Channels.ConnectionDemuxer+CompleteSingletonPreambleAndDispatchRequestAsyncResult.OnPreambleComplete(System.IAsyncResult)
   at System.Runtime.Fx+AsyncThunk.UnhandledExceptionFrame(System.IAsyncResult)
   at System.Runtime.AsyncResult.Complete(Boolean)
   at System.ServiceModel.Channels.ServerSingletonPreambleConnectionReader+CompletePreambleAsyncResult.OnWriteCompleted(System.Object)
   at System.ServiceModel.Channels.SocketConnection.OnSendAsync(System.Object, System.Net.Sockets.SocketAsyncEventArgs)
   at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
   at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
   at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(System.Net.Sockets.SocketError, Int32, System.Net.Sockets.SocketFlags)
   at System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)
   at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32, UInt32, System.Threading.NativeOverlapped*)

我们正在使用从“sqlite-netFx45-binary-bundle-Win32-2012-1.0.92.0”(从http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki下载)获取的SQLite程序集。这些程序集作为Windows服务的一部分捆绑在一起,不在GAC中。这种行为在32位和64位机器上都是一致的。请注意,我们没有使用混合模式程序集。
我们的连接字符串:
Data Source=ourapp.db;Version=3;New=False;Compress=True;PRAGMA cache_size=20000; PRAGMA page_size=32768; PRAGMA synchronous=off"

SQLite数据库文件位于Windows的“ProgramData”文件夹。

堆栈跟踪显示框架版本为“v4.0.30319”,而我们已经在服务的应用程序配置中明确设置了目标版本为4.5。但是,该计算机安装了这两个版本。

此外,我编写了一个简单的控制台应用程序,从多个线程调用相同的WCF服务方法,但无法模拟相同的AccessViolationException。因此,我认为这可能不是负载或并发访问相关问题。该异常似乎是随机的,我们没有办法始终重新生成该问题,除非只运行服务并等待它发生。

非常感谢任何有关可能导致此问题的指针。

更新:

使用的ExecuteCommand两个变体的代码:

    public int ExecuteCommand(string query, params DbParameter[] parameters)
    {
        try
        {
            this.result = -1;
            this.OpenConnection();
            this.command = new SQLiteCommand(query, this.connnection);
            this.HandleParameters(parameters);
            this.result = this.command.ExecuteNonQuery();
        }
        catch (Exception ex)
        {
            this.result = -1;
        }
        finally
        {
            if (this.command != null)
            {
                this.command.Dispose();
            }

            this.CloseConnection();
        }

        return this.result;
    }


    public int ExecuteCommand(Collection<string> queries, Collection<DbParameter[]> parameters)
    {
        try
        {
            this.result = -1;
            this.OpenConnection();
            this.command = new SQLiteCommand();
            this.command.Connection = this.connnection;
            this.transaction = this.connnection.BeginTransaction();

            for (int i = 0; i < queries.Count; i++)
            {
                this.command.Parameters.Clear();
                this.command.CommandText = queries[i];
                this.command.CommandTimeout = this.timeOut;
                this.command.Transaction = this.transaction;

                DbParameter[] cmdParams = new DbParameter[] { };
                if (parameters != null)
                {
                    cmdParams = parameters[i];
                }

                this.HandleParameters(cmdParams);
                this.result += this.command.ExecuteNonQuery();
            }

            this.transaction.Commit();
        }
        catch (Exception ex)
        {
            if (this.transaction != null)
            {
                this.transaction.Rollback();
            }

            this.result = -1;
        }
        finally
        {
            if (this.command != null)
            {
                this.command.Dispose();
            }

            this.CloseConnection();
        }

        return this.result;
    }

更新2:保存方法的代码

    Collection<DbParameter[]> dbparameters = new Collection<DbParameter[]>();
    DbParameter[] dbparams;
    SQLiteParameter sqlparams;
    Collection<string> queries = new Collection<string>();
    int icount = 0;

    foreach (Operation operation in operations)
    {
        icount = 0;
        dbparams = new DbParameter[4];

        queries.Add("UPDATE table1 SET col1 = @Col1, col2 = @col2, " +
                    "Timestamp = @Timestamp WHERE Id = @Id");

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Timestamp";
        sqlparams.Value = string.Format(CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss}", operation.Timestamp);
        dbparams[icount++] = sqlparams;

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Id";
        sqlparams.Value = operation.Id;
        dbparams[icount++] = sqlparams;

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Col1";
        sqlparams.Value = operation.Col1;
        dbparams[icount++] = sqlparams;

        sqlparams = new SQLiteParameter();
        sqlparams.DbType = DbType.String;
        sqlparams.ParameterName = "@Col2";
        sqlparams.Value = operation.Col2;
        dbparams[icount++] = sqlparams;


        dbparameters.Add(dbparams);
    }


    return (DataAccess.Sqlite.ExecuteCommand(queries, dbparameters) > -1);

你能展示一下 DataAccess.SqlLite.ExecuteCommandData.Settings.Save 方法的代码吗? - nvoigt
我已经更新了帖子,加入了ExecuteCommand的代码。Save方法组合查询及其参数,并调用重载方法,该方法接受一组查询。 - Ananth
SQLite自带PDB。您可以激活本地调试(或使用WinDbg),并查看在sqlite3_bind_int中导致问题的行。这可能会给您一些关于其中发生了什么的提示。 - Simon Mourier
2
SQLite核心不强制类型;但是,System.Data.SQLite将使用表模式中声明的列类型来转换列值。例如,如果一个列被声明为“INT”,则将使用Int32 CLR类型;但是,如果一个列被声明为“INTEGER”,则将使用Int64 CLR类型。让“INT”和“INTEGER”引用两种不同的基础CLR类型不是最佳选择;但是,由于向后兼容性,现在无法更改。 - Paul Zahra
1
你好,更好地理解异常的第一步是在catch(Exception ex)语句中添加日志记录。你可以使用适合你需要的任何库进行日志记录。 你没有看到任何异常的原因是代码正在进行异常吞噬(http://msdn.microsoft.com/en-us/library/ms229005(v=vs.85).aspx) 一旦开始记录日志,你就会了解到原始问题的上下文(这似乎是将列与传递的对象绑定时出现错误)。 希望这可以帮助你。 - wacdany
显示剩余2条评论
2个回答

1

你的类可能不是线程安全的。请检查多个线程是否同时调用了Save方法。

另外,请确保如果SQLiteConnection对象是你类的私有成员,实现IDisposable以进行处理。


1
错误与提供程序失去追踪开放的连接有关:
  • 尝试关闭不存在的连接
  • 尝试使用不存在的连接
错误看起来像是连接池的问题,但是您的连接字符串不使用连接池,并且它默认关闭。
还有其他任何东西使用打开了连接池的相同数据库吗?
首先要做的事情是记录异常。

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