SQLite keeps the database locked even after the connection is closed
I had the same problem using the datasets/tableadapters generated with the designer shipped with System.Data.Sqlite.dll
version 1.0.82.0 -- after closing the connection we were unable to read the database file using System.IO.FileStream
. I was disposing correctly both connection and tableadapters and I was not using connection pooling.
According to my first searches (for example this and this thread) that seemed a problem in the library itself -- either objects not correctly released and/or pooling issues (which I don't use).
After reading your question I tried to replicate the problem using only SQLiteCommand objects and I found that the problem arises when you don't dispose them. Update 2012-11-27 19:37 UTC: this is further confirmed by this ticket for System.Data.SQLite, in which a developer explains that "all SQLiteCommand and SQLiteDataReader objects associated with the connection [should be] properly disposed".
I then turned back on the generated TableAdapters and I saw that there was no implementation of the Dispose
method -- so in fact the created commands were not disposed. I implemented it, taking care of disposing all the commands, and I have got no problem.
Here's the code in C#, hope this helps. Please note that the code is converted from the original in Visual Basic, so expect some conversion errors.
//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();
}
}
}
}
Update 2013-07-05 17:36 UTC gorogm's answer highlights two important things:
according to the changelog on the official site of System.Data.SQLite, starting from version 1.0.84.0 the above code should not be needed, since the library takes care of this. I haven't tested this, but in the worst case you only need this snippet:
//In Table Adapter
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
this.Adapter.Dispose();
}about the implementation of the
Dispose
call of theTableAdapter
: it is is better to put this in a partial class, so that a dataset regeneration does not affected this code (and any additional code you may need to add).
SQLite locking file even though connection is closed
I'm late to the party, but I had to:
GC.WaitForPendingFinalizers();
GC.Collect();
Prior to calling File.Delete().
SQLite database locked after showing a DataGrid
Fundamentally, your code leaves several other things undisposed: the DbCommand
, DataAdapter
and most importantly, an odd DataReader
is created at the end which remains open and alive. Anything which implements the Dispose
method ought to be created and used in a Using
block to be sure it is disposed and resources released.
This is especially important with SQLite since the DB is just a file.
For basic add/change/delete apps your can create a DataAdapter
which will live for the life of your app/form and be able to perform all the DB operations for you:
' form/class level objects
Private daLite As SQLiteDataAdapter
Private dtLite As DataTable
Initializing the DB provider objects:
' spell out your SQL
Dim SQL = "SELECT Id, Name, Fish, Bird, Color, Price FROM Sample"
' persistent datatadapter
daLite = New SQLiteDataAdapter(SQL, LiteConnStr)
dtLite = New DataTable
' teach the DA how to do everything
Dim cb As New SQLiteCommandBuilder(daLite)
daLite.InsertCommand = cb.GetInsertCommand
daLite.UpdateCommand = cb.GetUpdateCommand
daLite.DeleteCommand = cb.GetDeleteCommand
' load schema, table
daLite.FillSchema(dtLite, SchemaType.Source)
daLite.Fill(dtLite)
dgv1.DataSource = dtLite
Passing the connection string rather than a connection object, lets the DataAdapter
create its own connection which it will open and close as needed. At the end, it knows how to add, insert and delete from the database because it "holds onto" those command objects. You may not need any other SQL in the app.
Note that each row in the DataTable
has a RowState
which indicates if it is added, deleted or changed so you can accumulate several changes before updating the database:
' dataadapter will open its connection, then close
Dim rows = daLite.Update(dtLite)
All the new rows are added, changed ones are updated, deleted ones are deleted. rows
will be the total number of them.
Another benefit is that you will be able to "refresh" the rows. If changes are being made outside the app (such as by you in a UI DB browser). Performing daLite.Fill(dtLite)
again later, picks up just those changes.
Notes
- In this case you would not dispose of the DBCommand, DBConnection etc in the procedure (or anywhere) because the plan is to use it over and over.
- You should spell out your SQL to control the order of the DGV column display.
- For the above to work, the Primary Key needs to be part of the SQL.
- Incidentally, changing the DGV
HeaderText
doesnt change the column name, just the displayed text. - When using something like this for bulk imports, a
Transaction
will speed it up.
This only works on simple one table queries as yours appears to be.
OperationalError: database is locked
From django doc:
SQLite is meant to be a lightweight
database, and thus can't support a
high level of concurrency.
OperationalError: database is locked
errors indicate that your application
is experiencing more concurrency than
sqlite can handle in default
configuration. This error means that
one thread or process has an exclusive
lock on the database connection and
another thread timed out waiting for
the lock the be released.Python's SQLite wrapper has a default
timeout value that determines how long
the second thread is allowed to wait
on the lock before it times out and
raises the OperationalError: database
is locked error.If you're getting this error, you can
solve it by:
- Switching to another database backend. At a certain point SQLite becomes too "lite" for real-world applications, and these sorts of concurrency errors indicate you've reached that point.
- Rewriting your code to reduce concurrency and ensure that database transactions are short-lived.
- Increase the default timeout value by setting the timeout database option
http://docs.djangoproject.com/en/dev/ref/databases/#database-is-locked-errorsoption
How do I unlock a SQLite database?
In windows you can try this program http://www.nirsoft.net/utils/opened_files_view.html to find out the process is handling db file. Try closed that program for unlock database
In Linux and macOS you can do something similar, for example, if your locked file is development.db:
$ fuser development.db
This command will show what process is locking the file:
> development.db: 5430
Just kill the process...
kill -9 5430
...And your database will be unlocked.
Python SQLite: database is locked
Turned out the problem happened because the path to the db file was actually a samba mounted dir. I moved it and that started working.
Related Topics
Html.Enumdropdownlistfor: Showing a Default Text
Is There a Serializable Generic Key/Value Pair Class in .Net
Wpf - Set Focus When a Button Is Clicked - No Code Behind
How to Add Event Handler for Dynamically Created Controls at Runtime
Login to the Page with Httpwebrequest
Customattribute Reflects HTML Attribute MVC5
How to Make Observablecollection Thread-Safe
Dynamic Lang. Runtime VS Reflection
Cannot Load Counter Name Data Because an Invalid Index -Exception
C# Reflection - Load Assembly and Invoke a Method If It Exists
Enumerating Collections That Are Not Inherently Ienumerable
Change the Value in App.Config File Dynamically
Is Async Httpclient from .Net 4.5 a Bad Choice for Intensive Load Applications
How to Find the Assembly System.Web.Extensions Dll
How to Get a Count of the Total Number of Digits in a Number