SQLite AccessViolationException в службе WCF

У нас есть служба Windows.NET, предоставляющая сервис WCF для пользовательского интерфейса и других частей нашей системы. Он нацелен на .NET Framework 4.5 и использует двоичные файлы SQLite 1.0.92 для общения с базой данных SQLite. Тем не менее, служба Windows аварийно завершает работу (автоматически останавливается) после запуска в течение некоторого времени с помощью AccessViolationException (найденного через Windows Event Viewer) в SQLite.Interop.dll. Я столкнулся с статьями, в которых говорится об этом исключении в 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 из "sqlite-netFx45-binary-bundle-Win32-2012-1.0.92.0" (скачанного с http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki). Эти сборки объединены как часть службы Windows и не находятся в GAC. Такое поведение совместимо как в 32-битных, так и в 64-битных машинах. FYI, мы не используем сборки смешанного режима.

Наша строка подключения:

Data Source=ourapp.db;Version=3;New=False;Compress=True;PRAGMA cache_size=20000; PRAGMA page_size=32768; PRAGMA synchronous=off"

и файл базы данных SQLite является папкой "ProgramData" Windows.

В стеке отображается версия Framework как "v4.0.30319", в то время как мы явно установили целевую версию в 4.5 в конфигурацию нашего приложения-службы. Однако на машине установлены обе версии.

Кроме того, я написал простое консольное приложение, которое вызывает один и тот же метод службы WCF из нескольких потоков, но не может имитировать одно и то же AccessViolationException. Следовательно, я не думаю, что это может быть проблема с загрузкой или одновременным доступом. Исключение кажется случайным, и у нас нет способа последовательно перепрофилировать проблему, кроме как запускать службу и ждать ее.

Приветствуются любые указания на то, что может вызвать эту проблему.

UPDATE:

Код для двух вариантов использования 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);

Ответ 1

Ошибка согласуется с тем, что поставщик потерял трек, из которого открыты соединения:

  • Попытка закрыть соединение, которое не существует
  • Попытка использовать соединение, которое не существует

Ошибка похожа на проблему с пулом Connection, но строка Connection не использует Connection pooling и по умолчанию отключена.

Что-нибудь еще Доступ к той же базе данных С включением пула соединений?

Первое, что вы должны сделать, это зарегистрировать исключение.

Ответ 2

Ваш класс не может быть потокобезопасным. Убедитесь, что несколько потоков не вызывают метод Save одновременно.

Также убедитесь, что вы реализуете IDisposable для удаления объекта SQLiteConnection, если он является частным членом вашего класса.