System.Data.SQLite Close () non rilascia il file di database

Sto riscontrando un problema nel chiudere il mio database prima di un tentativo di eliminare il file. Il codice è giusto

myconnection.Close(); File.Delete(filename); 

E l’eliminazione genera un’eccezione che il file è ancora in uso. Ho re-provato il Delete () nel debugger dopo pochi minuti, quindi non è un problema di temporizzazione.

Ho un codice di transazione ma non funziona affatto prima della chiamata a Close (). Quindi sono abbastanza sicuro che non si tratti di una transazione aperta. I comandi sql tra open e close sono solo selezionati.

ProcMon mostra il mio programma e il mio antivirus guardando il file del database. Non mostra il mio programma che rilascia il file db dopo la chiusura ().

Visual Studio 2010, C #, System.Data.SQLite versione 1.0.77.0, Win7

Ho visto un bug di due anni proprio come questo ma il log delle modifiche dice che è corretto.

C’è qualcos’altro che posso controllare? C’è un modo per ottenere un elenco di comandi o transazioni aperti?


Nuovo codice funzionante:

  db.Close(); GC.Collect(); // yes, really release the db bool worked = false; int tries = 1; while ((tries < 4) && (!worked)) { try { Thread.Sleep(tries * 100); File.Delete(filename); worked = true; } catch (IOException e) // delete only throws this on locking { tries++; } } if (!worked) throw new IOException("Unable to close file" + filename); 

Incontrato lo stesso problema qualche tempo fa mentre scrivevo un livello di astrazione DB per C # e non ho mai avuto il tempo di scoprire quale fosse il problema. Ho appena finito di lanciare un’eccezione quando hai tentato di cancellare un DB SQLite usando la mia libreria.

Ad ogni modo, questo pomeriggio ho guardato di nuovo tutto e ho pensato di provare a scoprire perché lo stava facendo una volta per tutte, quindi ecco cosa ho trovato finora.

Quello che succede quando chiami SQLiteConnection.Close() è che (insieme a un numero di controlli e altre cose) lo SQLiteConnectionHandle che punta all’istanza del database SQLite è disposto. Ciò avviene tramite una chiamata a SQLiteConnectionHandle.Dispose() , tuttavia in realtà questo non rilascia il puntatore fino a quando Garbage Collector di CLR non esegue una raccolta di dati inutili. Poiché SQLiteConnectionHandle sovrascrive la funzione CriticalHandle.ReleaseHandle() per richiamare sqlite3_close_interop() (tramite un’altra funzione), ciò non chiude il database.

Dal mio punto di vista questo è un pessimo modo di fare le cose dal momento che il programmatore non è sicuro quando il database viene chiuso, ma è così che è stato fatto quindi suppongo che per ora dobbiamo conviverci o impegnarsi alcune modifiche a System.Data.SQLite. Tutti i volontari sono invitati a farlo, sfortunatamente sono fuori tempo per farlo prima del prossimo anno.

TL; DR La soluzione è forzare un GC dopo la chiamata a SQLiteConnection.Close() e prima della chiamata a File.Delete() .

Ecco il codice di esempio:

 string filename = "testFile.db"; SQLiteConnection connection = new SQLiteConnection("Data Source=" + filename + ";Version=3;"); connection.Close(); GC.Collect(); GC.WaitForPendingFinalizers(); File.Delete(filename); 

Buona fortuna, e spero che aiuti

Solo GC.Collect() non ha funzionato per me.

Ho dovuto aggiungere GC.WaitForPendingFinalizers() dopo GC.Collect() per procedere con la cancellazione del file.

Nel mio caso stavo creando oggetti SQLiteCommand senza eliminarli esplicitamente.

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

Ho avvolto il mio comando in una dichiarazione using e ha risolto il mio problema.

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

L’istruzione using assicura che venga chiamato Dispose anche se si verifica un’eccezione.

Quindi è molto più semplice eseguire anche i comandi.

 value = connection.ExecuteScalar(commandText) // Command object created and disposed 

Quanto segue ha funzionato per me:

 MySQLiteConnection.Close(); SQLite.SQLiteConnection.ClearAllPools() 

Ulteriori informazioni : Le connessioni sono raggruppate da SQLite per migliorare le prestazioni. Significa che quando si chiama il metodo Close su un object di connessione, la connessione al database potrebbe essere ancora triggers (in background) in modo che il prossimo metodo Open diventi più veloce. non si desidera più una nuova connessione, chiamando ClearAllPools si chiudono tutte le connessioni che sono attive sullo sfondo e l’handle del file (s?) al file db viene rilasciato. Quindi il file db può essere rimosso, cancellato o utilizzato da un altro processo.

Aveva un problema simile, anche se la soluzione di garbage collector non ha risolto il problema.

Trovato lo smaltimento degli oggetti SQLiteCommand e SQLiteDataReader dopo l’uso mi ha salvato usando il garbage collector.

 SQLiteCommand command = new SQLiteCommand(sql, db); command.ExecuteNonQuery(); command.Dispose(); 

Stavo avendo un problema simile, ho provato la soluzione con GC.Collect ma, come notato, può richiedere molto tempo prima che il file non venga bloccato.

Ho trovato una soluzione alternativa che prevede lo smaltimento dei SQLiteCommand sottostanti nei TableAdapters, vedere questa risposta per ulteriori informazioni.

Usa GC.WaitForPendingFinalizers()

Esempio:

 Con.Close(); GC.Collect();` GC.WaitForPendingFinalizers(); File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB"); 

Prova questo … questo prova tutti i codici sopra … ha funzionato per me

  Reader.Close() connection.Close() GC.Collect() GC.WaitForPendingFinalizers() command.Dispose() SQLite.SQLiteConnection.ClearAllPools() 

Spero possa aiutare

Ho avuto lo stesso problema con EF e System.Data.Sqlite .

Per quanto mi riguarda ho trovato che SQLiteConnection.ClearAllPools() e GC.Collect() ridurrebbe la frequenza con cui si verificherà il blocco dei file, ma potrebbe comunque accadere occasionalmente (circa l’1% delle volte).

Ho indagato e sembra che alcuni SQLiteCommand che EF crea non siano disposti e abbiano ancora la loro proprietà Connection impostata sulla connessione chiusa. Ho provato a smaltirli, ma Entity Framework DbContext un’eccezione durante la successiva lettura di DbContext – sembra che EF a volte li usi ancora dopo la connessione chiusa.

La mia soluzione era assicurarsi che la proprietà Connection fosse impostata su Null quando la connessione si SQLiteCommand su questi SQLiteCommand . Questo sembra essere sufficiente per rilasciare il blocco dei file. Ho provato il codice sottostante e non ho visto alcun problema di blocco dei file dopo alcune migliaia di test:

 public static class ClearSQLiteCommandConnectionHelper { private static readonly List OpenCommands = new List(); public static void Initialise() { SQLiteConnection.Changed += SqLiteConnectionOnChanged; } private static void SqLiteConnectionOnChanged(object sender, ConnectionEventArgs connectionEventArgs) { if (connectionEventArgs.EventType == SQLiteConnectionEventType.NewCommand && connectionEventArgs.Command is SQLiteCommand) { OpenCommands.Add((SQLiteCommand)connectionEventArgs.Command); } else if (connectionEventArgs.EventType == SQLiteConnectionEventType.DisposingCommand && connectionEventArgs.Command is SQLiteCommand) { OpenCommands.Remove((SQLiteCommand)connectionEventArgs.Command); } if (connectionEventArgs.EventType == SQLiteConnectionEventType.Closed) { var commands = OpenCommands.ToList(); foreach (var cmd in commands) { if (cmd.Connection == null) { OpenCommands.Remove(cmd); } else if (cmd.Connection.State == ConnectionState.Closed) { cmd.Connection = null; OpenCommands.Remove(cmd); } } } } } 

Per usare basta chiamare ClearSQLiteCommandConnectionHelper.Initialise(); all’inizio del carico dell’applicazione. Ciò manterrà quindi un elenco di comandi attivi e imposterà la loro connessione su Null quando puntano a una connessione chiusa.

Credo che la chiamata a SQLite.SQLiteConnection.ClearAllPools() sia la soluzione più pulita. Per quanto ne so non è corretto chiamare manualmente GC.Collect() nell’ambiente WPF. Sebbene, non ho notato il problema finché non sono stato aggiornato a System.Data.SQLite 1.0.99.0 in 3/2016

Stavo lottando con il problema simile. Vergogna su di me … ho finalmente capito che Reader non era chiuso. Per qualche ragione stavo pensando che il Reader sarà chiuso quando la connessione corrispondente sarà chiusa. Ovviamente, GC.Collect () non ha funzionato per me.
Wrapping the Reader con “using: statement” è anche una buona idea. Ecco un rapido codice di test.

 static void Main(string[] args) { try { var dbPath = "myTestDb.db"; ExecuteTestCommand(dbPath); File.Delete(dbPath); Console.WriteLine("DB removed"); } catch (Exception e) { Console.WriteLine(e.Message); } Console.Read(); } private static void ExecuteTestCommand(string dbPath) { using (var connection = new SQLiteConnection("Data Source=" + dbPath + ";")) { using (var command = connection.CreateCommand()) { command.CommandText = "PRAGMA integrity_check"; connection.Open(); var reader = command.ExecuteReader(); if (reader.Read()) Console.WriteLine(reader.GetString(0)); //without next line database file will remain locked reader.Close(); } } } 

Forse non hai affatto bisogno di trattare con GC. Per favore, controlla se tutto sqlite3_prepare è finalizzato.

Per ogni sqlite3_prepare , è necessario un corrispondente sqlite3_finalize .

Se non si finalizza correttamente, sqlite3_close non chiuderà la connessione.

Stavo usando SQLite 1.0.101.0 con EF6 e ho avuto problemi con il file bloccato dopo tutte le connessioni e le quadro disposte.

Ciò è peggiorato con gli aggiornamenti di EF che mantenevano chiuso il database dopo che erano stati completati. GC.Collect () è stata l’unica soluzione che mi ha aiutato e stavo cominciando a disperare.

Disperato, ho provato ClearSQLiteCommandConnectionHelper di Oliver Wickenden (vedi la sua risposta dell’8 luglio). Fantastico. Tutti i problemi di blocco sono andati! Grazie Oliver.

Aveva un problema simile. Chiamare Garbage Collector non mi ha aiutato. LAter Ho trovato un modo per risolvere il problema

L’autore ha anche scritto che ha fatto query SELECT a quel database prima di provare a eliminarlo. Ho la stessa situazione

Ho il codice seguente:

 SQLiteConnection bc; string sql; var cmd = new SQLiteCommand(sql, bc); SQLiteDataReader reader = cmd.ExecuteReader(); reader.Read(); reader.Close(); // when I added that string, the problem became solved. 

Inoltre, non è necessario chiudere la connessione al database e chiamare Garbage Collector. Tutto quello che dovevo fare era chiudere il lettore creato durante l’esecuzione della query SELECT

In attesa di Garbage Collector non è ansible rilasciare il database sempre e ciò che è successo a me. Quando si verifica un qualche tipo di Eccezione nel database SQLite, ad esempio se si tenta di inserire una riga con il valore esistente per PrimaryKey, il file del database verrà conservato fino a quando non lo si elimina. Il seguente codice intercetta l’eccezione SQLite e annulla il comando problematico.

 SQLiteCommand insertCommand = connection.CreateCommand(); try { // some insert parameters insertCommand.ExecuteNonQuery(); } catch (SQLiteException exception) { insertCommand.Cancel(); insertCommand.Dispose(); } 

Se non gestisci le eccezioni dei comandi problematici di Garbage Collector non puoi fare nulla su di loro perché ci sono alcune eccezioni non gestite su questi comandi in modo che non siano spazzatura. Questo metodo di gestione ha funzionato bene per me con l’attesa per il garbage collector.