C #: база данных SQLite всегда заблокирована

#c# #sqlite

#c# #sqlite

Вопрос:

Я написал простое приложение (назовем его app1), которое считывает базу данных SQLite и отображает содержимое в виде сетки. У меня есть отдельное консольное приложение C # (app2), которому необходимо выполнять запись в ту же базу данных. Проблема в том, что app2 завершается сбоем с ошибкой «база данных заблокирована». Я вижу, как только я запускаю app1, создается файл userdb-journal. Я предполагаю, что проблема в том, что app1 открывает базу данных, но не освобождает ее? Это код, который у меня есть для заполнения таблицы, которую я привязываю к сетке в app1.

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

Вот код, который заполняет gridview:

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

    }
  

Как я могу исправить ситуацию, чтобы app2 мог читать и записывать в базу данных во время работы app1?

РЕДАКТИРОВАТЬ Похоже, что файл журнала создается только app2. Я заметил ошибку блокировки базы данных только при запуске app1, но, возможно, app1 — отвлекающий маневр. Приложение 2 является многопоточным. Возможно, мне следует задать новый вопрос, посвященный app2 и многопоточному доступу?

РЕДАКТИРОВАТЬ Спасибо за все комментарии. Я установил блокировку вокруг всех обращений к БД и включил все в usings. Кажется, теперь все работает.

Комментарии:

1. Журнал SQLite создается при записи в базу данных, а не при ее чтении. И GetAllPeople выполняет только чтение из базы данных, поэтому, вероятно, на мгновение получает только одну общую блокировку, а затем возвращается. Вы уверены, что не выполняете запись в свою базу данных в своем app1?

2. Вы правы. Я был неправ. Похоже, что этот файл журнала создается только app2. Я заметил ошибку блокировки базы данных только при запуске app1, но, возможно, app1 — отвлекающий маневр. Приложение 2 является многопоточным. Возможно, мне следует задать новый вопрос, посвященный app2 и многопоточному доступу?

3. @RogerS: вам следует сосредоточиться на той части, где вы выполняете запись в базу данных, то есть на той части, где она блокируется. Проверьте настроенные тайм-ауты чтения и убедитесь, что вы правильно размещаете свои подключения. Скорее всего, есть часть вашего кода, которая выполняет длительную операцию вставки или обновления и слишком долго блокирует файл.

4. @Groo — Я ужесточил порядок доступа к БД с помощью блокировок и инструкций using, и, похоже, все работает. Спасибо.

5. Я не уверен, что это значит «я устанавливаю блокировку на все обращения к БД». Однако для меня это звучит проблематично. Когда вы выполняете доступ к базе данных, вы выполняете свою блокировку, а затем sqlite выполняет повторный просмотр? Это приводит к ненужному снижению производительности и проблемам с обслуживанием.

Ответ №1:

Вот код, легко задайте параметры в построителе строк подключения и создайте SQLiteConnection с его помощью.

  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()))
        {
            //...
        }
  

С уважением.

Ответ №2:

Вы просили SQLITE подождать и повторить попытку, если база данных заблокирована? Вот как это сделать на C

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

Дело в том, что SQLITE ненадолго блокирует базу данных при обращении к ней. Если другой поток или процесс обращается к ней, будучи заблокированным, SQLITE по умолчанию возвращает ошибку. Но вы можете заставить ее подождать и повторить попытку автоматически с помощью вышеупомянутого вызова. Это решает многие из проблем такого рода.

Комментарии:

1. Спасибо, это выглядит очень полезным. Вы знаете, как это сделать на C #?

2. @RogerS Я не часто использую c #. Мое предположение: добавьте ‘busy_timeout = 100’ в строку вашего подключения.

3. Немного поздно, но на всякий случай, если это кому-то понадобится: если вы используете System.Data.SQLite.dll вы можете использовать класс SQLiteConnectionStringBuilder и его свойство DefaultTimeout по умолчанию, чтобы определить время ожидания.

4. @D.Rosado Я предлагаю вам опубликовать ответ с некоторым примером кода, показывающим, как это сделать. Кто знает, возможно, OP вернется и примет ваш ответ.