This post is to try to find the best way to avoid the SQLite exceptions "Locked" or "Busy" in an enviroment with concurrency using BeginTransaction, Commit, Close and Dispose.
As we know, if there are two or more operations in the database at the same time, it is possible to get the errors Locked" or "Busy".
I did see that the best way to work in an enviroment with concurrency is create the connection once and never close, but using this way I'm get other errors when I try to crete a new Transaction when I'm in one, so I need to commit first before begin a new transaction.
For a company request, we need to use the "Rollback Transaction" when one DB operation fails, for that reason I'm using BeginTransaction, Commit, Close and Dispose.
This are a resume of the methos that I'm using.
Connection in Shared project:
public static SQLiteConnection conn;
public static SQLiteConnection ObtenerConexion()
{
conn = DependencyService.Get<AccesoDatosI>().ObtenerConexion();
return conn;
}
Connection in Android project:
public SQLite.SQLiteConnection ObtenerConexion()
{
lock (semaforo)
{
var sqliteFileName = "BD.db3";
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var path = Path.Combine(documentsPath, sqliteFileName);
var conn = new SQLite.SQLiteConnection(path, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);
return conn;
}
}
Logic method:
public async void Save(List<Office> lista)
{
try
{
// Get the conn and begin the transaction
BeginTransaction();
// Objeto DAO.
OfficeDAO objOfficeDAO = new OfficeDAO(this.Connection);
// We make the DB operation.
objOfficeDAO.Save(this.Connection, lista);
// We apply the operation. Commit and Close Connection.
ApplyTransaction(true);
}
catch (Exception e)
{
UndoTransaction(true, e);
throw e;
}
}
Methods that interacts with the database:
// Save list of objects
public void Save(SQLiteConnection connection, List<Office> lista)
{
try
{
connection.InsertAll(lista);
}
catch (Exception e)
{
throw e;
}
}
// We begin the transaction.
public static SQLiteConnection BeginTransaction(SQLiteConnection connection)
{
connection.BeginTransaction();
return connection;
}
// We make the commit.
public static void ApplyTransaction(SQLiteConnection connection, bool close)
{
connection.Commit();
if (close)
{
CloseConnection(connection);
}
}
// We close connection.
public static void CloseConnection(SQLiteConnection connection)
{
connection.Close();
connection.Dispose();
}
// We undo the trasaction.
public static void UndoTransaction(SQLiteConnection connection, bool close)
{
connection.Rollback();
if (close)
{
CloseConnection(connection);
}
}
I think that I did everything to avoid thats errors, but I can't find a solution to this using the BeginTransaction and Dispose the connection.
We are try to find the best way to solve this using the SQLite tools without to create another solution, I mean, we try to SQLite can deal with this erros without create another solution that don't involve SQLite.
I'm going to glad to read your comments.
Thanks in advance.