System.Data.SQLite не поддерживает множественные транзакции - PullRequest
14 голосов
/ 19 июля 2011

Итак, у меня возникла интересная проблема с System.Data.SQLite и я использую несколько транзакций. В основном у меня есть следующий код, который не работает:

using (IDbConnection connection1 = new SQLiteConnection("connectionstring"), connection2 = new SQLiteConnection("connectionstring"))
{
    connection1.Open();
    connection2.Open();

    IDbTransaction transaction1 = connection1.BeginTransaction();
    IDbTransaction transaction2 = connection2.BeginTransaction();    // Fails!

    using(IDbCommand command = new SQLiteCommand())
    {
        command.Text = "CREATE TABLE artist(artistid int, artistname text);";
        command.CommandType = CommandType.Text;
        command.Connection = connection1;
        command.ExecuteNonQuery();
    }

    using (IDbCommand command = new SQLiteCommand())
    {
        command.Text = "CREATE TABLE track(trackid int, trackname text);";
        command.CommandType = CommandType.Text;
        command.Connection = connection2;                    
        command.ExecuteNonQuery();
    }

    transaction1.Commit();
    transaction2.Commit();

}

Из того, что я прочитал, кажется, что System.Data.SQLite должен поддерживать вложенные и, следовательно, последовательные транзакции. Сбой кода в строке 7 (где объявлена ​​вторая транзакция), за исключением следующего:

System.Data.SQLite.SQLiteException: The database file is locked

System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
System.Data.SQLite.SQLiteDataReader.NextResult()
System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()
System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock)
System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
System.Data.Common.DbConnection.System.Data.IDbConnection.BeginTransaction()

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

Спасибо!

Ответы [ 5 ]

14 голосов
/ 19 июля 2011

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

SQLiteConnection conn = new SQLiteConnection("data source=:memory:");
conn.Open();

var command = conn.CreateCommand();
command.CommandText = "create table a (b integer primary key autoincrement, c text)";
command.ExecuteNonQuery();

var tran1 = conn.BeginTransaction();
var tran2 = conn.BeginTransaction();

var command1 = conn.CreateCommand();
var command2 = conn.CreateCommand();

command1.Transaction = tran1;
command2.Transaction = tran2;

command1.CommandText = "insert into a VALUES (NULL, 'bla1')";
command2.CommandText = "insert into a VALUES (NULL, 'bla2')";

command1.ExecuteNonQuery();
command2.ExecuteNonQuery();

tran1.Commit();
tran2.Commit();

command.CommandText = "select count(*) from a";
Console.WriteLine(command.ExecuteScalar());
3 голосов
/ 19 июля 2011

SQLite спроектирован как облегченная база данных для таких вещей, как закладки в браузере или фотографии в программе фото-каталога.SQLite имеет очень детальную систему блокировки, которая оптимизирована для сценариев с большим количеством читателей или одним потоком чтения / записи.Как таковые, как известно, имеют проблемы с производительностью при одновременных операциях записи - он просто не предназначен для использования в приложениях со многими параллельными устройствами записи .Вы можете делать одновременную запись, но она не будет хорошо масштабироваться.

В этом случае проблема заключается в том, что вы пытаетесь внести параллельные изменения схемы - если вместо этого вы сделали несколько SELECTили несколько INSERT операторов, тогда это будет работать успешно (как показывает ответ sixfeetsix).

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

См. Блокировка файлов и параллелизм в SQLite версии 3 для более подробной информации.

1 голос
/ 03 января 2013

Попробуйте использовать:

((SQLiteConnection)connection).BeginTransaction(true)-

Параметр bool говорит о deferredLock.Но помните, что ExecuteNonScalar следует вызывать только после совершения первой транзакции.

0 голосов
/ 19 июля 2011

SQLite не поддерживает множественные транзакции - он блокирует всю базу данных во время транзакции (см. Www.sqlite.org).

EDIT: поддерживается несколько транзакций, но не при использовании DDL в более чем одной транзакции.

0 голосов
/ 19 июля 2011

Для чего стоит, не поддерживая несколько транзакций для одного соединения, кажется обычным для провайдеров данных (я точно знаю ODP.NET, возможно, другие).

Один из способов обойти это - иметьотдельный IDbConnection для каждого отдельного IDbTransaction.

- РЕДАКТИРОВАТЬ ---

Doh!Я только что понял, что у вас есть несколько соединений.К сожалению.

...