SQLite Database Locked исключение
Я получаю База заблокированных исключений из SQLite только для некоторых запросов.
Ниже приведен мой код: когда я выполняю любой оператор выбора, он работает нормально.
Когда я выполняю любое заявление о записи Jobs
Таблица это тоже отлично работает.
Это отлично работает:
ExecuteNonQuery("DELETE FROM Jobs WHERE id=1");
Но так же, если я выполняю запросы для Employees
таблица выдает исключение, что база данных заблокирована.
Это создает исключение:
ExecuteNonQuery("DELETE FROM Employees WHERE id=1");
Ниже приведены мои функции:
public bool OpenConnection()
{
if (Con == null)
{
Con = new SQLiteConnection(ConnectionString);
}
if (Con.State == ConnectionState.Closed)
{
Con.Open();
//Cmd = new SQLiteCommand("PRAGMA FOREIGN_KEYS=ON", Con);
//Cmd.ExecuteNonQuery();
//Cmd.Dispose();
//Cmd=null;
return true;
}
if (IsConnectionBusy())
{
Msg.Log(new Exception("Connection busy"));
}
return false;
}
public Boolean CloseConnection()
{
if (Con != null && Con.State == ConnectionState.Open)
{
if (Cmd != null) Cmd.Dispose();
Cmd = null;
Con.Close();
return true;
}
return false;
}
public Boolean ExecuteNonQuery(string sql)
{
if (sql == null) return false;
try
{
if (!OpenConnection())
return false;
else
{
//Tx = Con.BeginTransaction(IsolationLevel.ReadCommitted);
Cmd = new SQLiteCommand(sql, Con);
Cmd.ExecuteNonQuery();
//Tx.Commit();
return true;
}
}
catch (Exception exception)
{
//Tx.Rollback();
Msg.Log(exception);
return false;
}
finally
{
CloseConnection();
}
}
Это исключение: в строке 103: Cmd.ExecuteNonQuery();
Обнаружено исключение: Тип: System.Data.SQLite.SQLiteException Сообщение: база данных заблокирована База данных заблокирована Источник: System.Data.SQLite
Stacktrace: в System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt) в System.Data.SQLite.SQLiteDataReader.NextResult() в System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior ведут себя) в System.Data.SQLite.SQLiteCommand.ExecuteReader(поведение CommandBehavior) в System.Data.SQLite.SQLiteCommand.ExecuteNonQuery() в TimeSheet6.DbOp.ExecuteNonQuery(String sql) в d:\Projects\C# Applications\Completed Projects\TimeSheet6\TimeSB. CS: линия 103
8 ответов
Где-то на этом пути соединение остается открытым. Избавляться от OpenConnection
а также CloseConnection
и изменить ExecuteNonQuery
к этому:
using (SQLiteConnection c = new SQLiteConnection(ConnectionString))
{
c.Open();
using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
{
cmd.ExecuteNonQuery();
}
}
Кроме того, измените способ чтения данных следующим образом:
using (SQLiteConnection c = new SQLiteConnection(ConnectionString))
{
c.Open();
using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
...
}
}
}
Не пытайтесь управлять пулом соединений самостоятельно, как вы здесь. Во-первых, это намного сложнее, чем то, что вы кодировали, но во-вторых, это уже внутри SQLiteConnection
объект. Наконец, если вы не используете using
вы не утилизируете эти объекты должным образом, и у вас возникают такие проблемы, как то, что вы видите сейчас.
Вы можете использовать оператор "using", как показано ниже, чтобы убедиться, что соединение и команда расположены правильно даже в исключительной ситуации.
private static void ExecuteNonQuery(string queryString)
{
using (var connection = new SQLiteConnection(
ConnectionString))
{
using (var command = new SQLiteCommand(queryString, connection))
{
command.Connection.Open();
command.ExecuteNonQuery();
}
}
}
Вы должны закрыть свой DataReader перед попыткой записи каких-либо данных в базу данных. Использование:
dr.Close();
после того, как вы закончите использовать DataReader.
В моем случае это было очень глупо с моей стороны, я вносил изменения в браузере SQLite и не нажимал на запись изменений, что заблокировало изменение БД службами. После того, как я нажал кнопку "Записать изменения", все запросы на публикацию работали, как ожидалось.
Здесь много полезных сообщений для людей, которые, возможно, забыли очистить оборванное соединение, но есть и другой способ, которым это может произойти: SQLite не поддерживает одновременные операции INSERT; если вы введете два INSERT одновременно, они будут обрабатываться последовательно. Когда INSERT выполняется быстро, это нормально, но если INSERT занимает больше времени, чем тайм-аут, второй INSERT может дать сбой с этим сообщением.
У меня это случилось, когда я использовал длительную транзакцию, чтобы собрать кучу INSERT в одну большую фиксацию. Обычно я блокировал базу данных от любых других действий во время транзакции. Переход наjournal_mode=WAL
разрешит одновременную запись и чтение, но не одновременную запись.
Я избавился от длительной транзакции и позволил каждой INSERT автоматически фиксироваться, и это решило мою проблему.
Моя была вызвана тем, что SqliteDataReader не закрывался при вызове HasRows().
У меня было это:
using (SQLiteConnection connection = new SQLiteConnection(DbPath))
{
connection.Open();
string sql = $"SELECT * FROM ...";
using (SQLiteCommand command = new SQLiteCommand(sql, connection))
{
return command.ExecuteReader().HasRows;
}
connection.Close();
}
Но нужно было использовать ExecuteReader следующим образом:
using (SQLiteDataReader reader = command.ExecuteReader())
{
return command.ExecuteReader().HasRows;
}
Несмотря на то, что DbConnection удалялся и воссоздавался каждый раз, когда читатель все еще оставался заблокированным читателем.
У меня была такая же проблема при загрузке большого количества данных в разные таблицы из нескольких потоков. При попытке выполнить вставку я блокировал базу данных, потому что программа выполняла слишком много вставок слишком быстро, и SQLite не успевал завершить каждую транзакцию до прихода другой.
Вставка выполняется через потоки, потому что я не хотел, чтобы интерфейс был заблокирован и ждал завершения вставки.
Мое решение — использовать BlockingCollection с ThreadPool.QueueUserWorkItem. Это позволяет мне освободить интерфейс при выполнении вставок. Все вставки ставятся в очередь и выполняются в порядке FIFO (First In First Out). Теперь база данных никогда не блокируется при выполнении любой транзакции SQL из любого потока.
public class DatabaseQueueBus
{
private BlockingCollection<TransportBean> _dbQueueBus = new BlockingCollection<TransportBean>(new ConcurrentQueue<TransportBean>());
private CancellationTokenSource __dbQueueBusCancelToken;
public CancellationTokenSource _dbQueueBusCancelToken { get => __dbQueueBusCancelToken; set => __dbQueueBusCancelToken = value; }
public DatabaseQueueBus()
{
_dbQueueBusCancelToken = new CancellationTokenSource();
DatabaseQueue();
}
public void AddJob(TransportBean dto)
{
_dbQueueBus.Add(dto);
}
private void DatabaseQueue()
{
ThreadPool.QueueUserWorkItem((param) =>
{
try
{
do
{
string job = "";
TransportBean dto = _dbQueueBus.Take(_dbQueueBusCancelToken.Token);
try
{
job = (string)dto.DictionaryTransBean["job"];
switch (job)
{
case "SaveClasse":
//Save to table here
break;
case "SaveRegistrant":
//Save Registrant here
break;
}
}
catch (Exception ex)
{//TODO: Handle this exception or not
}
} while (_dbQueueBusCancelToken.Token.IsCancellationRequested != true);
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
}
});
}
}
Вставки выполняются таким образом, но без очереди я все еще сталкивался с проблемой блокировки.
using (SQLiteConnection c = new SQLiteConnection(BaseDal.SQLiteCon))
{
c.Open();
using (SQLiteCommand cmd = new SQLiteCommand(sql, c))
{
cmd.ExecuteNonQuery();
}
c.Close();
}
Я также получил ту же ошибку здесь:
if (new basics.HindiMessageBox(HMsg, HTitle).ShowDialog()==true)
{
SQLiteConnection m_dbConnection = new SQLiteConnection(MainWindow.con);
m_dbConnection.Open();
sql = "DELETE FROM `users` WHERE `id`=" + SelectedUser.Id;
command = new SQLiteCommand(sql, m_dbConnection);
command.ExecuteNonQuery();
m_dbConnection.Close();
LoadUserDG();
}
но когда я только что поменял место объявления SQLiteConnection
public partial class User : Window
{
SQLiteCommand command;
string sql;
AddUser AddUserObj;
List<basics.users> usersList;
basics.users SelectedUser;
SQLiteConnection m_dbConnection;
// ...
private void DeleteBtn_Click(object sender, RoutedEventArgs e)
{
// ...
if (new basics.HindiMessageBox(HMsg, HTitle).ShowDialog()==true)
{
m_dbConnection = new SQLiteConnection(MainWindow.con);
m_dbConnection.Open();
sql = "DELETE FROM `users` WHERE `id`=" + SelectedUser.Id;
command = new SQLiteCommand(sql, m_dbConnection);
command.ExecuteNonQuery();
m_dbConnection.Close();
LoadUserDG();
}
}
сейчас все хорошо. надеюсь, что это может сработать и для вас. если кто-то может сказать, как это происходит, скажите, что я хотел бы узнать, чтобы улучшить свои знания.