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

сейчас все хорошо. надеюсь, что это может сработать и для вас. если кто-то может сказать, как это происходит, скажите, что я хотел бы узнать, чтобы улучшить свои знания.

Другие вопросы по тегам