2011-04-27 3 views
4

J'ai écrit une application simple (appelez-la app1) qui lit une base de données SQLite et affiche le contenu dans une grille. J'ai une application de console C# séparée (app2) qui doit écrire dans la même base de données. Le problème est que app2 échoue avec une erreur "database is locked". Je peux voir dès que je démarre app1 un fichier journal userdb est créé. Je suppose que le problème est que app1 ouvre la base de données mais ne la libère pas? C'est le code que j'ai pour remplir la Table I liée à la grille dans app1.C#: base de données SQLite toujours verrouillée

public DataTable GetAllPeople() 
    { 
     var connectionString = "Data Source=" + dbPath + ";Version=3"; 

     using (SQLiteDataAdapter sqlDataAdapter = 
      new SQLiteDataAdapter("SELECT id,FirstName,LastName,Address FROM Users", 
            connectionString)) 
     { 
      using (DataTable dataTable = new DataTable()) 
      { 
       sqlDataAdapter.Fill(dataTable); 
       // code to add some new columns here 

       return dataTable; 
      } 
     } 
    } 

Voici le code qui renseigne le gridview:

private void Form1_Load(object sender, EventArgs e) 
    { 
     UserDatabase db = new UserDatabase(); 
     db.Initialize(); 
     dataGridView1.DataSource = db.GetAllPeople(); 

    } 

Comment puis-je arranger les choses si App2 peut lire et écrire dans la base de données en cours d'exécution app1 est?

EDIT On dirait que ce fichier journal est uniquement créé par app2. J'avais seulement remarqué l'erreur verrouillée de base de données quand app1 fonctionnait également, mais peut-être app1 est un hareng rouge. App2 est multi-thread. Peut-être devrais-je commencer une nouvelle question en mettant l'accent sur app2 et l'accès multithread?

EDIT Merci pour tous les commentaires. J'ai mis un verrou autour de tous les accès db et j'ai tout enveloppé dans les fichiers. Tout semble fonctionner maintenant.

+0

Le journal SQLite est créé lorsque vous * écrivez * dans la base de données, et non lorsque vous le lisez. Et 'GetAllPeople' ne lit que depuis la base de données, donc il n'acquiert probablement qu'un seul verrou partagé pendant un moment, puis retourne. Êtes-vous sûr de ne pas écrire sur votre base de données dans votre application1? – Groo

+0

Vous avez raison. J'avais tort. On dirait que ce fichier journal est créé uniquement par app2. J'avais seulement remarqué l'erreur verrouillée de base de données quand app1 fonctionnait également, mais peut-être app1 est un hareng rouge. App2 est multi-thread. Peut-être devrais-je commencer une nouvelle question en mettant l'accent sur app2 et l'accès multithread? – RogerS

+0

@RogerS: vous devriez vous concentrer sur la partie où vous écrivez dans la base de données, c'est-à-dire la partie où elle est verrouillée. Vérifiez les délais d'attente de lecture configurés et assurez-vous que vous disposez de vos connexions correctement. Il y a probablement une partie de votre code qui fait une longue opération d'insertion ou de mise à jour et verrouille le fichier trop longtemps. – Groo

Répondre

0

Avez-vous demandé à SQLITE d'attendre et de réessayer si la base de données est verrouillée? Voici comment le faire en C

// set SQLite to wait and retry for up to 100ms if database locked 
    sqlite3_busy_timeout(db, 100); 

Le fait est que SQLITE verrouille le db brièvement quand on y accède. Si un autre thread ou processus l'accède alors qu'il est bloqué, SQLITE renvoie par défaut une erreur. Mais vous pouvez le faire attendre et réessayer automatiquement avec l'appel ci-dessus. Cela résout beaucoup de ces types de problèmes.

+0

Merci cela semble très utile. Savez-vous comment faire cela à partir de C#? – RogerS

+0

@RogerS Je n'utilise pas beaucoup de C#. Ma conjecture: ajoutez 'busy_timeout = 100' à votre chaîne de connexion. – ravenspoint

+0

Bit en retard mais juste au cas où quelqu'un en aurait besoin: si vous utilisez System.Data.SQLite.dll, vous pouvez utiliser la classe SQLiteConnectionStringBuilder et sa propriété DefaultTimeout pour définir le délai. –

2

Voici le code, définissez facilement les paramètres sur le générateur de chaîne de connexion et construisez le SQLiteConnection avec lui.

SQLiteConnectionStringBuilder connBuilder = new SQLiteConnectionStringBuilder(); 
     connBuilder.DataSource = filePath; 
     connBuilder.Version = 3; 
     connBuilder.CacheSize = 4000;    
     connBuilder.DefaultTimeout = 100; 
     connBuilder.Password = "mypass"; 


     using(SQLiteConnection conn = new SQLiteConnection(connBuilder.ToString())) 
     { 
      //... 
     } 

Cordialement.

Questions connexes