#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 вернется и примет ваш ответ.