2014-05-20 20 views
12

Abbiamo un servizio Windows .NET che espone un servizio WCF a un'interfaccia utente e ad altre parti del nostro sistema. È indirizzato a .NET Framework 4.5 e utilizza i binari SQLite 1.0.92 per comunicare con il database SQLite sottostante. Tuttavia, il servizio di Windows si arresta in modo anomalo (arrestato automaticamente) dopo essere stato eseguito per qualche tempo con una AccessViolationException (trovata tramite il Visualizzatore eventi di Windows) in SQLite.Interop.dll. Mi sono imbattuto in articoli che parlano di questa eccezione in Connection close, ma in tutti i nostri casi riscontriamo questa eccezione durante l'interrogazione o la scrittura nel DB utilizzando i metodi esposti dal nostro servizio WCF. Lo stack-trace è il seguente:SQLite AccessViolationException nel servizio 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*) 

viene eseguita con i gruppi SQLite di "sqlite-netFx45-binario-fascio-Win32-2012-1.0.92.0" (scaricato da http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki). Gli assembly sono raggruppati come parte del servizio di Windows e non sono in GAC. Questo comportamento è coerente in entrambe le macchine a 32 e 64 bit. A proposito, NON stiamo usando gli assembly in modalità mista.

nostra stringa di connessione:

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

e il file di database SQLite è la cartella di Windows "ProgramData".

Lo stacktrace mostra la versione Framework come "v4.0.30319" mentre abbiamo impostato esplicitamente la versione di destinazione su 4.5 nella configurazione dell'applicazione del nostro servizio. Tuttavia, la macchina ha entrambe le versioni installate.

Inoltre, ho scritto una semplice applicazione console che richiama lo stesso metodo di servizio WCF da più thread, ma non è in grado di simulare la stessa AccessViolationException. Quindi, non penso che potrebbe essere un carico o un problema relativo all'accesso simultaneo. L'eccezione sembra casuale e non abbiamo modo di riprodurre costantemente il problema se non quello di eseguire il servizio e attendere che accada.

Qualsiasi suggerimento su ciò che potrebbe causare questo problema è molto apprezzato.

UPDATE:

codice per le due varianti di ExecuteCommand utilizzati:

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; 
    } 

UPDATE 2: Codice per Save metodo

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); 
+0

Potete mostrare qualche codice per i metodi 'DataAccess.SqlLite.ExecuteCommand' e' Data.Settings.Save'? – nvoigt

+0

Ho aggiornato il post con il codice per ExecuteCommand. Il metodo Save compone query e relativi parametri e chiama il metodo overload che accetta una raccolta di query. – Ananth

+0

Un altro aggiornamento per il metodo Save – Ananth

risposta

1

L'errore è coerente con la pista fornitore perdere di cui Connessioni aperte:

  • cercando di chiudere una connessione che non esiste
  • Cercando di utilizzare una connessione che non esiste

L'errore si presenta come un problema con pool di connessioni, ma la stringa di connessione non fa uso di pool di connessioni e è disattivato per impostazione predefinita.

Qualsiasi altra cosa accede allo stesso database Con il pool di connessioni attivato?

La prima cosa che devi fare è registrare l'eccezione.

1

La tua classe potrebbe non essere thread sicuro.Verifica che thread multipli non richiamino il metodo Save contemporaneamente.

Assicurati inoltre di implementare IDisposable per smaltire l'oggetto SQLiteConnection se è un membro privato della classe.