120 votes

System.Data.SQLite Close() ne libère pas le fichier de base de données

J'ai un problème pour fermer ma base de données avant d'essayer de supprimer le fichier. Le code est juste

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

Et l'application Delete lève une exception en indiquant que le fichier est toujours en cours d'utilisation. J'ai réessayé le Delete() dans le débogueur après quelques minutes, ce n'est donc pas un problème de timing.

J'ai le code de la transaction mais il ne s'exécute pas du tout avant l'appel à Close(). Je suis donc presque sûr qu'il ne s'agit pas d'une transaction ouverte. Les commandes sql entre open et close sont juste des selects.

ProcMon montre mon programme et mon antivirus en train d'examiner le fichier de la base de données. Il ne montre pas que mon programme libère le fichier db après le close().

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

J'ai vu un bug vieux de deux ans exactement comme celui-ci, mais le changelog dit qu'il est corrigé.

Y a-t-il autre chose à vérifier ? Existe-t-il un moyen d'obtenir une liste des commandes ou des transactions en cours ?


Nouveau, code de travail :

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

9voto

edymtt Points 1085

J'ai eu un problème similaire, j'ai essayé la solution avec GC.Collect mais, comme indiqué, il peut s'écouler beaucoup de temps avant que le fichier ne devienne non verrouillé.

J'ai trouvé une solution alternative qui implique l'élimination de l'élément sous-jacent. SQLiteCommand dans les TableAdapters, voir cette réponse pour plus d'informations.

6voto

Hallupa Points 195

J'ai eu le même problème avec EF et System.Data.Sqlite .

Pour moi, j'ai trouvé SQLiteConnection.ClearAllPools() y GC.Collect() réduirait la fréquence des blocages de fichiers, mais cela se produirait encore occasionnellement (environ 1 % du temps).

J'ai fait des recherches et il semblerait que certaines SQLiteCommand créées par EF ne sont pas éliminées et leur propriété Connection est toujours définie sur la connexion fermée. J'ai essayé de les éliminer, mais Entity Framework lançait alors une exception lors de la prochaine opération d'élimination de la connexion. DbContext lire - il semble qu'EF les utilise encore parfois après la fermeture de la connexion.

Ma solution a été de m'assurer que la propriété Connection est définie sur Null lorsque la connexion se ferme sur ces SQLiteCommand s. Cela semble suffire pour débloquer le verrouillage du fichier. J'ai testé le code ci-dessous et je n'ai constaté aucun problème de verrouillage de fichier après quelques milliers de tests :

public static class ClearSQLiteCommandConnectionHelper
{
    private static readonly List<SQLiteCommand> OpenCommands = new List<SQLiteCommand>();

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

Pour l'utiliser, il suffit d'appeler ClearSQLiteCommandConnectionHelper.Initialise(); au début du chargement de la demande. Il conservera alors une liste des commandes actives et définira leur connexion à Null lorsqu'ils pointent vers une connexion fermée.

5voto

Bishnu Dev Points 62

Essayez ceci... celui-ci tente tout ce qui précède codes ... a fonctionné pour moi

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

J'espère que cela vous aidera

5voto

Sharad Kishor Points 41

Utilice GC.WaitForPendingFinalizers()

Exemple :

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

3voto

Dokug Points 11

La raison semble être une fonctionnalité appelée "Pooling". L'ajout de "Pooling=false" à la chaîne de connexion entraîne la libération du fichier DB avec "connection.Close()".

Voir la FAQ sur la mise en commun des connexions ici : https://www.devart.com/dotconnect/sqlite/docs/FAQ.html#q54

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X