Подтвердить что ты не робот

SQLite блокирует базу данных даже после закрытия соединения

Я использую поставщика System.Data.SQLite в приложении ASP.NET(фреймворк 4.0). Проблема, с которой я сталкиваюсь, заключается в том, что когда я вставляю что-то в таблицу базы данных SQLite, база данных блокируется, и блокировка не освобождается даже после того, как соединение установлено.

При попытке доступа к файлу ошибка: "Процесс не может получить доступ к файлу" catalog.sqlite ", потому что он используется другим процессом".

Мой код довольно прост, я открываю соединение, читаю некоторые данные из базы данных SQLServer, вставляю эти данные в SQLite (через SQLiteDataAdapter), а затем закрываю соединение и удаляю все, чтобы быть в безопасности. Но все же я получаю эту ошибку, когда пытаюсь закрепить файл после заполнения данными.

Я прочитал все предложения здесь, в StackOverflow, но ни один из них не помог решить проблему (отключив антивирус, изменив модель транзакции, ожидая несколько секунд, прежде чем закрепить файл, обернув все вставки в транзакция и т.д., но никто не помог решить эту проблему.

Может быть, что-то особенное для ASP.NET(многопоточность является проблемой? Хотя я тестирую ее на машине разработки, где есть только один вызов этой функции и concurrency?)

В качестве побочного примечания я попытался избежать DataTable и SQLiteDataAdapter и использовать только SQLiteCommand напрямую, и таким образом это работает. Конечно, я могу продолжать строить свои запросы в виде строк вместо использования адаптеров данных, но мне кажется, что это немного неудобно, когда есть инфраструктура, созданная для этого.

4b9b3361

Ответ 1

У меня была та же проблема с использованием наборов данных/таблиц-адаптеров, созданных с помощью конструктора, поставляемого с System.Data.Sqlite.dll версии 1.0.82.0 - после закрытия соединения мы не смогли прочитать файл базы данных, используя System.IO.FileStream. Я правильно распределял подключения и табличные адаптеры и не использовал пул подключений.

Согласно моим первым поискам (например, этот и этот поток), которые казались проблемой в самой библиотеке - либо объекты не были правильно освобождены, и/или проблемы с пулами (которые я не не использовать).

Прочитав ваш вопрос, я попытался воспроизвести проблему, используя только объекты SQLiteCommand, и обнаружил, что проблема возникает, когда вы не избавляетесь от них. Обновление 2012-11-27 19:37 UTC: это подтверждается этим билетом для System.Data.SQLite, в котором разработчик объясняет, что "все объекты SQLiteCommand и SQLiteDataReader связаны с соединением [должно быть] правильно утилизировано ".

Затем я снова включил сгенерированные адаптеры таблиц и увидел, что реализации метода Dispose не было, поэтому фактически созданные команды не были расположены. Я реализовал это, позаботившись об удалении всех команд, и у меня нет проблем.

Вот код на С#, надеюсь, это поможет. Обратите внимание, что код конвертируется из оригинала в Visual Basic, поэтому следует ожидать некоторых ошибок преобразования.

//In Table Adapter    
protected override void Dispose(bool disposing)
{
   base.Dispose(disposing);

    Common.DisposeTableAdapter(disposing, _adapter, _commandCollection);
}

public static class Common
{
    /// <summary>
    /// Disposes a TableAdapter generated by SQLite Designer
    /// </summary>
    /// <param name="disposing"></param>
    /// <param name="adapter"></param>
    /// <param name="commandCollection"></param>
    /// <remarks>You must dispose all the command,
    /// otherwise the file remains locked and cannot be accessed
    /// (for example, for reading or deletion)</remarks>
    public static void DisposeTableAdapter(
        bool disposing,
        System.Data.SQLite.SQLiteDataAdapter adapter,
        IEnumerable<System.Data.SQLite.SQLiteCommand> commandCollection)
    {
        if (disposing) {
            DisposeSQLiteTableAdapter(adapter);

            foreach (object currentCommand_loopVariable in commandCollection)
            {
                currentCommand = currentCommand_loopVariable;
                currentCommand.Dispose();
            }
        }
    }

    public static void DisposeSQLiteTableAdapter(
            System.Data.SQLite.SQLiteDataAdapter adapter)
    {
        if (adapter != null) {
            DisposeSQLiteTableAdapterCommands(adapter);

            adapter.Dispose();
        }
    }

    public static void DisposeSQLiteTableAdapterCommands(
            System.Data.SQLite.SQLiteDataAdapter adapter)
    {
        foreach (object currentCommand_loopVariable in {
            adapter.UpdateCommand,
            adapter.InsertCommand,
            adapter.DeleteCommand,
            adapter.SelectCommand})
        {
            currentCommand = currentCommand_loopVariable;
            if (currentCommand != null) {
                currentCommand.Dispose();
            }
        }
    }
}

Обновление 2013-07-05 17:36 UTC Вответе горогма выделены две важные вещи:

  • Согласно журналу изменений на официальном сайте System.Data.SQLite, начиная с версии 1.0.84.0, приведенный выше код не требуется, поскольку об этом заботится библиотека. Я не проверял это, но в худшем случае вам нужен только этот фрагмент:

    //In Table Adapter    
    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);
    
      this.Adapter.Dispose();
    }
    
  • о реализации вызова Dispose в TableAdapter: лучше поместить это в частичный класс, чтобы регенерация набора данных не повлияла на этот код (и любой дополнительный код, который вам, возможно, понадобится добавить).

Ответ 2

У меня та же проблема. Мой сценарий состоял в том, что после получения данных в файле базы данных SQLite я хочу удалить этот файл, но он всегда выдает ошибку "... используя другой процесс". Даже я удаляю SqliteConnection или SqliteCommand, ошибка все еще происходит. Я исправил ошибку, вызвав GC.Collect().

Фрагмент кода

public void DisposeSQLite()
{
    SQLiteConnection.Dispose();
    SQLiteCommand.Dispose();

    GC.Collect();
}

Надеюсь на эту помощь.

Ответ 3

В моем случае я создавал объекты SQLiteCommand, не используя их явно.

var command = connection.CreateCommand();
command.CommandText = commandText;
value = command.ExecuteScalar();

Я завернул свою команду в инструкции using и исправил мою проблему.

static public class SqliteExtensions
{
    public static object ExecuteScalar(this SQLiteConnection connection, string commandText)
    {
        // Added using
        using (var command = connection.CreateCommand())
        {
            command.CommandText = commandText;
            return command.ExecuteScalar();
        }
    }
}

Тогда вы можете использовать его так:

connection.ExecuteScalar(commandText);

Ответ 4

Следующие работали для меня: MySQLiteConnection.Close(); SQLite.SQLiteConnection.ClearAllPools()

Ответ 5

В большинстве случаев проблема возникает, если вы не распоряжаетесь своими читателями и командами должным образом. Существует сценарий, в котором команды и считыватели не будут правильно размещаться.

Сценарий 1: Если вы используете функцию boolean. до достижения результата код в блоке finally не будет отменен. Это большая проблема, если вы собираетесь оценивать результаты функции isDataExists при выполнении кода, если он соответствует результату i.e

    if(isDataExists){
        // execute some code
    }

Оцениваемая функция

    public bool isDataExists(string sql)
    {
        try
        {
            OpenConnection();
            SQLiteCommand cmd = new SQLiteCommand(sql, connection);
            reader = cmd.ExecuteReader();
            if (reader != null && reader.Read())
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        catch (Exception expMsg)
        {
            //Exception
        }
        finally
        {
            if (reader != null)
            {
                reader.Dispose();
            }
            CloseConnection();
        }
        return true;
    }

Решение: Уберите ваш читатель и команду внутри блока try следующим образом

            OpenConnection();
            SQLiteCommand cmd = new SQLiteCommand(sql, connection);
            reader = cmd.ExecuteReader();
            if (reader != null && reader.Read())
            {
                cmd.Dispose();
                CloseConnection();
                return true;
            }
            else
            {
                cmd.Dispose();
                CloseConnection();
                return false;
            }

Наконец удалите читателя и команду на всякий случай, если что-то пошло не так.

        finally
        {
            if (reader != null)
            {
                reader.Dispose();
            }
            CloseConnection();
        }

Ответ 6

Я нашел, что edymtt правильно ответил на обвинение TableAdapters/Datasets, но вместо того, чтобы модифицировать каждый раз, заново созданный файл кода TableAdapter, я нашел другое решение: вручную вызвать. Установить на дочерние элементы TableAdapter. (В .NET 4.5, последний SQLite 1.0.86)

using (var db = new testDataSet())
{
    using (testDataSetTableAdapters.UsersTableAdapter t = new testDataSetTableAdapters.UsersTableAdapter())
    {
        t.Fill(db.Users);
        //One of the following two is enough
        t.Connection.Dispose(); //THIS OR
        t.Adapter.Dispose();    //THIS LINE MAKES THE DB FREE
    }
    Console.WriteLine((from x in db.Users select x.Username).Count());
}

Ответ 7

Как сказано ранее, объекты SQLite должны быть уничтожены. Однако есть странное поведение: соединение должно быть открыто во время вызова Dispose on commands. Например:

using(var connection = new SqliteConnection("source.db"))
{
    connection.Open();
    using(var command = connection.CreateCommand("select..."))
    {
        command.Execute...
    }
}

работает нормально, но:

using(var connection = new SqliteConnection("source.db"))
{
    connection.Open();
    using(var command = connection.CreateCommand("select..."))
    {
        command.Execute...
        connection.Close();
    }
}

дает тот же файл блокировки

Ответ 8

Это был один из лучших результатов Google, который я нашел, когда я столкнулся с этой ошибкой. Тем не менее, ни один из ответов не помог мне так после дальнейшего поиска и поиска в Google. Я придумал этот код, который работает из некоторого кода из http://www.tsjensen.com/blog/post/2012/11/10/SQLite-on-Visual-Studio-with-NuGet-and-Easy-Instructions.aspx

Однако мне не пришлось вообще использовать NuGet. То, что делает моя программа, - это загрузка файла db с сервера при каждом его открытии. Затем, если пользователь обновит этот db, он будет загружен для всех, чтобы в следующий раз открыть одну и ту же программу. Я получаю сообщение об ошибке, которое файл использовал после обновления локального файла и пытается загрузить его в наш SharePoint. Теперь он отлично работает.

Public Function sqLiteGetDataTable(sql As String) As DataTable
    Dim dt As New DataTable()
    Using cnn = New SQLiteConnection(dbConnection)
        cnn.Open()
        Using cmd As SQLiteCommand = cnn.CreateCommand()
            cmd.CommandText = sql
            Using reader As System.Data.SQLite.SQLiteDataReader = cmd.ExecuteReader()
                dt.Load(reader)
                reader.Dispose()
            End Using
            cmd.Dispose()
        End Using
        If cnn.State <> System.Data.ConnectionState.Closed Then
            cnn.Close()
        End If
        cnn.Dispose()
    End Using
    Return dt
End Function

Ответ 9

Обеспечение надлежащего удаления любых IDisposable (например, SQLiteConnection, SQLiteCommand и т.д.) решает эту проблему. Я должен повторить повторение того, что нужно использовать "использование" в качестве привычки для обеспечения надлежащего удаления одноразовых ресурсов.

Ответ 10

У меня была та же проблема, и она была исправлена ​​только путем утилизации DbCommand в инструкции using, но с Pooling = true моя проблема была исправлена!

                SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder
                {
                    Pooling = true
                };

Ответ 11

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

В любом случае у меня были все вещи, такие как закрытие соединений, ClearAllPools и т.д., Но мне не хватало aTableAdapter.Adapter.Dispose(), и это исправило это.

Ответ 12

Была такая же проблема. Решил это просто установив SQLite 1.0.111 (через nuget).

Не нужно ничего менять в моем коде, только обновление до этой версии. Ранее я использовал 1.0.85, где файл был заблокирован, хотя соединение было закрыто и удалено.