2012-09-21 46 views
30

Estoy usando el proveedor de System.Data.SQLite en una aplicación ASP.NET (framework 4.0). El problema al que me estoy enfrentando es que cuando INSERTO algo en una tabla en la base de datos SQLite, la base de datos se bloquea y el bloqueo no se libera incluso después de que se elimina la conexión.SQLite mantiene la base de datos bloqueada incluso después de que se cierra la conexión

Al intentar acceder al archivo, el error es: "El proceso no puede acceder al 'catalog.sqlite' archivo porque está siendo utilizado por otro proceso."

Mi código es bastante sencillo, abro la conexión, leo datos de una base de datos SQLServer, inserto esos datos en SQLite (a través de SQLiteDataAdapter) y luego cierro la conexión y dispongo todo para estar seguro. Pero aún así, obtengo ese error cuando intento comprimir el archivo una vez que se ha completado con los datos.

He leído todo tipo de sugerencias aquí en StackOverflow pero ninguna de ellas me ha ayudado a resolver el problema (apagar el antivirus, cambiar el modelo de transacción, esperar unos segundos antes de subir el archivo, ajustar todas las llamadas de inserción en una transacción, etc., pero ninguno ha ayudado a resolver este problema.

Quizás haya algo específico en ASP.NET (¿qué es el multihilo? Aunque lo estoy probando en una máquina de desarrollo donde solo hay una llamada esa función y no la concurrencia?)

Como nota al margen, traté de evitar DataTable y SQLiteDataAdapter y el uso de SQLiteCommand directamente y de esa manera funciona un ch brazo. Por supuesto, puedo seguir creando mis consultas como cadenas en lugar de utilizar los adaptadores de datos, pero me resulta un poco incómodo cuando se construye un marco para hacerlo.

+2

¿Está ajustando los comandos y las conexiones en las instrucciones de uso? – Arran

+0

¿Esta aplicación está bloqueada, es decir, la cerradura se desvanece cuando sale? –

+0

@Arran No estoy envolviendo la conexión con el uso. Pero no lo estoy incluyendo incluso con la versión que funciona sin bloqueo (también conocida como la que usa comandos en lugar de adaptadores de datos). –

Respuesta

31

Tuve el mismo problema al usar datasets/tableadapters generados con el diseñador incluido con System.Data.Sqlite.dll versión 1.0.82.0 - después de cerrar la conexión no pudimos leer el archivo de base de datos usando System.IO.FileStream. Estaba eliminando correctamente tanto la conexión como los adaptadores de tabla y no estaba usando la agrupación de conexiones.

Según mis primeras búsquedas (por ejemplo this y this thread) que parecían un problema en la propia biblioteca - no hayan sido puestos en libertad los objetos correctamente y/o problemas de puesta en común (que yo no uso).

Después de leer su pregunta traté de reproducir el problema utilizando sólo SQLiteCommand objetos y encontré que el problema surge cuando no se deshaga de ellos. Actualización 27/11/2012 19:37 UTC: Esto se ve confirmado por this ticket para System.Data.SQLite, en el que un desarrollador explica que "todos SQLiteCommand y SQLiteDataReader objetos asociados a la conexión [deben] desechan ".

Luego volví a los TableAdapters generados y vi que no había una implementación del método Dispose, por lo que, de hecho, los comandos creados no se descartaban. Lo implementé, teniendo cuidado de eliminar todos los comandos, y no tengo ningún problema.

Aquí está el código en C#, espero que esto ayude. Tenga en cuenta que el código se convierte a partir del original in Visual Basic, por lo que se esperan algunos errores de conversión.

//In Table Adapter  
protected override void Dispose(bool disposing) 
{ 
    base.Dispose(disposing); 

    Common.DisposeTableAdapter(disposing, _adapter, _commandCollection); 
} 

public static class Common 
{ 
    /// <summary> 
    /// Disposes a TableAdapter generated by SQLite Designer 
    /// </summary> 
    /// <param name="disposing"></param> 
    /// <param name="adapter"></param> 
    /// <param name="commandCollection"></param> 
    /// <remarks>You must dispose all the command, 
    /// otherwise the file remains locked and cannot be accessed 
    /// (for example, for reading or deletion)</remarks> 
    public static void DisposeTableAdapter(
     bool disposing, 
     System.Data.SQLite.SQLiteDataAdapter adapter, 
     IEnumerable<System.Data.SQLite.SQLiteCommand> commandCollection) 
    { 
     if (disposing) { 
      DisposeSQLiteTableAdapter(adapter); 

      foreach (object currentCommand_loopVariable in commandCollection) 
      { 
       currentCommand = currentCommand_loopVariable; 
       currentCommand.Dispose(); 
      } 
     } 
    } 

    public static void DisposeSQLiteTableAdapter(
      System.Data.SQLite.SQLiteDataAdapter adapter) 
    { 
     if (adapter != null) { 
      DisposeSQLiteTableAdapterCommands(adapter); 

      adapter.Dispose(); 
     } 
    } 

    public static void DisposeSQLiteTableAdapterCommands(
      System.Data.SQLite.SQLiteDataAdapter adapter) 
    { 
     foreach (object currentCommand_loopVariable in { 
      adapter.UpdateCommand, 
      adapter.InsertCommand, 
      adapter.DeleteCommand, 
      adapter.SelectCommand}) 
     { 
      currentCommand = currentCommand_loopVariable; 
      if (currentCommand != null) { 
       currentCommand.Dispose(); 
      } 
     } 
    } 
} 

Actualizar 07/05/2013 17:36 UTCgorogm's answer destaca dos cosas importantes:

  • de acuerdo con la changelog en el sitio oficial de System.Data.SQLite, a partir de la versión 1.0.84.0 el código anterior no debería ser necesario, ya que la biblioteca se ocupa de esto. No he probado esto, pero en el peor de los casos sólo se necesita este fragmento:

    //In Table Adapter  
    protected override void Dispose(bool disposing) 
    { 
        base.Dispose(disposing); 
    
        this.Adapter.Dispose(); 
    } 
    
  • sobre la aplicación de la llamada de la DisposeTableAdapter: es es mejor poner esto en una clase parcial, por lo que la regeneración de un conjunto de datos no afecta a este código (y a cualquier código adicional que pueda necesitar agregar).

+0

Veo fugas de memoria muy grandes usando Entity framework y el último 1.82.0 sqlite. ¿Crees que este es el problema? – Peter

+0

Probablemente, dado que (creo que tengo poca experiencia en EF) podría haber recursos no administrados relacionados con 'SQLiteCommand's esperando a ser lanzados. [Este hilo SO] (http://stackoverflow.com/questions/8511901/system-data-sqlite-close-not-releasing-database-file) y [este ticket] (http: //system.data.sqlite. org/index.html/tktview?name = ab8b0ccbf6) parece corroborar su hipótesis. Para evitar las filtraciones, podría intentar deshabilitar "múltiples conjuntos de resultados activos" en EF o podría intentar usar una biblioteca administrada como [C# -SQLite] (http://code.google.com/p/csharp-sqlite/) . Espero que esto ayude. – edymtt

+0

Esto es resolver mi problema, gracias –

22

Tengo el mismo problema. Mi escenario fue después de recibir los datos dentro del archivo de base de datos SQLite Quiero borrar ese archivo pero siempre emite un error "... usando por otro proceso". Incluso dispongo de SqliteConnection o SqliteCommand, el error aún ocurre. Solucioné el error llamando al GC.Collect().

Fragmento de código

public void DisposeSQLite() 
{ 
    SQLiteConnection.Dispose(); 
    SQLiteCommand.Dispose(); 

    GC.Collect(); 
} 

Esperanza esta ayuda.

+0

He tenido el mismo problema también y tuve que usar GC.Collect(); se refieren a: http://system.data.sqlite.org/index.html/tktview/6434e23a0f63b440?plaintext Esperemos que la próxima versión de SQLite va a resolver este problema. – peterincumbria

+0

myyyyyyyyyy dios !!!! ¡¡¡GRACIAS!!! ¡¡¡DE VERDAD!!! ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡DÍAS EMOCIONANTES A BUSCAR RESPUESTAS PERO NADIE VIENE CON EL GC. Recoger(); ¿QUÉ FUNCIONÓ? omfg ... GRACIAS! – Assassinbeast

+2

@klaydze @Assassinbeast una forma más limpia de tratar el problema que menciona es llamar 'System.Data.SQLite.SQLiteConnection.ClearAllPools();' en lugar de las dos llamadas a deshacerse + GC.Colgar –

8

En mi caso estaba creando objetos SQLiteCommand sin eliminarlos explícitamente.

var command = connection.CreateCommand(); 
command.CommandText = commandText; 
value = command.ExecuteScalar(); 

envolví mi mando en un comunicado using y fijo mi problema.

static public class SqliteExtensions 
{ 
    public static object ExecuteScalar(this SQLiteConnection connection, string commandText) 
    { 
     // Added using 
     using (var command = connection.CreateCommand()) 
     { 
      command.CommandText = commandText; 
      return command.ExecuteScalar(); 
     } 
    } 
} 

A continuación, se puede utilizar de esta manera

connection.ExecuteScalar(commandText); 
+2

¡Recomiendo encarecidamente no tragar excepciones como esta! –

+1

Misma persona, mismo comentario, respuesta diferente. Reparado, gracias. – Nate

+0

¿Cómo exactamente esto traga excepciones? Cuando uso esto obtengo un lanzamiento de excepción al final de la instrucción using si el comando falla. – Miebster

1

encontré la respuesta de edymtt razón en culpar TableAdapters/Conjuntos de datos, pero en lugar de modificar el cada vez que re-generada CodeFile TableAdapter, me encontré con una otra solución : para llamar manualmente. Desinciar en elementos secundarios de TableAdapter. (En .NET 4.5, la última SQLite 1.0.86)

using (var db = new testDataSet()) 
{ 
    using (testDataSetTableAdapters.UsersTableAdapter t = new testDataSetTableAdapters.UsersTableAdapter()) 
    { 
     t.Fill(db.Users); 
     //One of the following two is enough 
     t.Connection.Dispose(); //THIS OR 
     t.Adapter.Dispose(); //THIS LINE MAKES THE DB FREE 
    } 
    Console.WriteLine((from x in db.Users select x.Username).Count()); 
} 
+0

No tengo que modificar 'TableAdapters' cuando cambio el conjunto de datos en el diseñador - He implementado' Dispose' usando [clases parciales] (http://msdn.microsoft.com/en-us/library/wa80x488 % 28v = vs.110% 29.aspx). Gracias por señalar que en las últimas versiones de System.Data.SQLite mi código ya no es necesario (ver [changelog] (http://system.data.sqlite.org/index.html/doc/trunk/www/news) .wiki) para la versión 1.0.84. – edymtt

1

En la mayoría de los casos el problema surgirá si no deseche sus lectores y los comandos correctamente. Hay un escenario en el que los comandos y lectores no se desharán correctamente.

Escenario 1: En caso de que se están ejecutando un booleano función . antes de llegar a un resultado, el código en el bloque finally no se ejecutará. Este es un gran problema si va a evaluar los resultados de la función isDataExists mientras ejecuta el código si le conviene al resultado.e

if(isDataExists){ 
     // execute some code 
    } 

La función se evalúa

public bool isDataExists(string sql) 
    { 
     try 
     { 
      OpenConnection(); 
      SQLiteCommand cmd = new SQLiteCommand(sql, connection); 
      reader = cmd.ExecuteReader(); 
      if (reader != null && reader.Read()) 
      { 
       return true; 
      } 
      else 
      { 
       return false; 
      } 
     } 
     catch (Exception expMsg) 
     { 
      //Exception 
     } 
     finally 
     { 
      if (reader != null) 
      { 
       reader.Dispose(); 
      } 
      CloseConnection(); 
     } 
     return true; 
    } 

Solución: disponer de su lector y comando dentro del bloque try de la siguiente manera

  OpenConnection(); 
      SQLiteCommand cmd = new SQLiteCommand(sql, connection); 
      reader = cmd.ExecuteReader(); 
      if (reader != null && reader.Read()) 
      { 
       cmd.Dispose(); 
       CloseConnection(); 
       return true; 
      } 
      else 
      { 
       cmd.Dispose(); 
       CloseConnection(); 
       return false; 
      } 

Finalmente disponer el lector y el orden de la misma en caso de que algo saliera mal

 finally 
     { 
      if (reader != null) 
      { 
       reader.Dispose(); 
      } 
      CloseConnection(); 
     } 
+0

Si descarta el comando en el bloque 'try', una excepción que ocurra durante' ExecuteReader() 'causará que el comando no se elimine. Debería usar bloques' using', o si prefiere escribirlos usted mismo, puede anidar múltiples bloques 'try' /' finally'. –

0

Este fue uno de los mejores resultados de google que encontré cuando me encontré con este error. Sin embargo, ninguna de las respuestas me ayudó después de más de buscar alrededor y buscando en Google me ocurrió con este código que funciona a partir de una parte del código de http://www.tsjensen.com/blog/post/2012/11/10/SQLite-on-Visual-Studio-with-NuGet-and-Easy-Instructions.aspx

Sin embargo, yo no tenía que usar el NuGet en absoluto. Lo que hace mi programa es descargar un archivo db de un servidor cada vez que se abre. Entonces, si un usuario actualiza ese db, se cargará para que todos lo puedan obtener la próxima vez que abran el mismo programa. Recibí el error de que el archivo estaba en uso después de actualizar el archivo local y de intentar cargarlo en nuestro SharePoint. Ahora funciona bien.

Public Function sqLiteGetDataTable(sql As String) As DataTable 
    Dim dt As New DataTable() 
    Using cnn = New SQLiteConnection(dbConnection) 
     cnn.Open() 
     Using cmd As SQLiteCommand = cnn.CreateCommand() 
      cmd.CommandText = sql 
      Using reader As System.Data.SQLite.SQLiteDataReader = cmd.ExecuteReader() 
       dt.Load(reader) 
       reader.Dispose() 
      End Using 
      cmd.Dispose() 
     End Using 
     If cnn.State <> System.Data.ConnectionState.Closed Then 
      cnn.Close() 
     End If 
     cnn.Dispose() 
    End Using 
    Return dt 
End Function 
5

Los siguientes trabajó para mí: MySQLiteConnection.Close(); SQLite.SQLiteConnection.ClearAllPools()

0

Asegurar que cualquier IDisposable (por ejemplo, SQLiteConnection, SQLiteCommand, etc) se eliminarse adecuadamente resuelve este problema. Debo repetir que uno debe usar "usar" como un hábito para asegurar la eliminación adecuada de los recursos desechables.

1

Como dijimos antes, los objetos SQLite deben ser destruidos. Sin embargo, hay un comportamiento extraño: la conexión debe estar abierta durante una llamada Dispose en los comandos. Por ejemplo:

using(var connection = new SqliteConnection("source.db")) 
{ 
    connection.Open(); 
    using(var command = connection.CreateCommand("select...")) 
    { 
     command.Execute... 
    } 
} 

funciona bien, pero:

using(var connection = new SqliteConnection("source.db")) 
{ 
    connection.Open(); 
    using(var command = connection.CreateCommand("select...")) 
    { 
     command.Execute... 
     connection.Close(); 
    } 
} 

da el mismo bloqueo de archivo

0

que tenían el mismo problema y que sólo se fijó disponiendo el DbCommand en la declaración using, pero con Pooling = true mi problema fue reparado !!

   SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder 
       { 
        Pooling = true 
       }; 
Cuestiones relacionadas