2011-07-19 62 views
14

所以我有一个有趣的问题与System.Data.SQLite和使用多个事务。基本上,我有以下的代码失败: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() 

有谁知道问题是什么或如何解决这个问题?我觉得并发事务对于任何数据库系统都是必不可少的,所以必须有一些方法来做到这一点。

谢谢!

+1

看起来非常非常紧密你的代码...在第一次使用声明你新'command1',但你参考'命令' – JonH

+0

Typo,让我修复 –

回答

14

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

对于它的价值,不支持每个单连接的多个事务似乎是数据提供者(我确实知道ODP.NET,可能是其他人)的常见情况。

解决此问题的一种方法是为每个单独的IDbTransaction单独提供IDbConnection

- 编辑---

Doh!我只是意识到你有多个连接。抱歉。

+0

sqlite支持多个事务。 – 2011-07-19 15:21:26

-1

SQLite不支持多个事务 - 它在事务中锁定整个数据库(请参阅www.sqlite.org)。

编辑: 支持多个事务,但在多个事务中使用DDL时不支持。

+0

这是不正确的。 – 2011-07-19 15:21:07

+0

当比较Oracle中的多个事务支持(例如SQLite)时,您会看到SQLite只是“模拟”对它的支持......您的示例和原始问题之间存在差异,您使用DML,并且他使用DDL这两个交易...我相应地调整了我的答案。 – Yahia

3

SQLite被设计为轻量级数据库,用于诸如浏览器中的书签或照片目录程序中的照片。 SQLite有一个非常精细的锁定系统,它针对具有多个阅读器或单个读写器线程的场景进行了优化。因此,它被称为具有并发写入的性能问题 - 它没有被设计用于具有许多共同作者的应用程序。你可以并发写入,但它不会很好地扩展。

在这种情况下,这个问题是因为你正试图使并发模式改变 - 如果你不是做多SELECT或多个INSERT语句那么这个工作成功(如sixfeetsix的答案所示)。

如果您的应用程序有很多读者并没有太多的作家则SQLite的可能是正常,但是如果你有许多并发读者和作家一个应用程序,那么你可能会发现一个完全成熟的数据库服务器更适合。

有关更多详细信息,请参阅File Locking And Concurrency In SQLite Version 3

+0

gee第三个答案就是这样... sqlite _does_支持多个事务。 – 2011-07-19 15:21:45

+0

@sixfeetsix我最近的编辑是否更清楚一点? – Justin

+1

是的,我现在正在考虑一点,因为我现在明白了你的观点,但我的观点是,如果我把自己放在OP的鞋子里,我发现我更喜欢社区更直接地解决我的问题,即坚持告诉我,多个写连接是问题,然后作为一个额外的讨论,为什么坚持只有一个事务可能会更好。 – 2011-07-19 15:37:58

1

尝试使用:

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

的布尔参数讲述了deferredLock。但是,请记住,只有在提交第一个事务后才应调用ExecuteNonScalar。