池化只读SQLite连接用于读写访问

时间:2015-10-20 08:31:41

标签: c# .net sqlite sqlite-net

使用的nuget包:System.Data.SQLite.Core,1.0.98.1

问题:在我的程序中,我在某些情况下使用SQLite启用池和只读访问。通常它工作正常,但是如果对数据库有很多混合的只读/读写密集请求,那么程序将失败并出现以下异常:

Unhandled Exception: System.Data.SQLite.SQLiteException: attempt to write a readonly database
attempt to write a readonly database
at System.Data.SQLite.SQLite3.Reset(SQLiteStatement stmt)
at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt)
at System.Data.SQLite.SQLiteDataReader.NextResult()
at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave)
at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery(CommandBehavior behavior)
at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery()

如果我禁用池,那么程序运行正常。我假设以某种方式池化只读连接用于读写连接。我是否会遗漏某些东西 - 也就是预期的行为?

要重现的最小代码(在INSERT或DELETE时失败)。如果我引入延迟,例如Thread.Sleep(10000),它可以正常工作。如果我删除循环,它也可以正常工作。

const string DbFilePath = "test.sqlite";
string readOnlyConnectionString = new SQLiteConnectionStringBuilder
    {
        DataSource = DbFilePath,
        Pooling = true,
        ReadOnly = true
    }.ConnectionString; // data source=test.sqlite;pooling=True;read only=True
string readWriteConnectionString = new SQLiteConnectionStringBuilder
    {
        DataSource = DbFilePath,
        Pooling = true,
        ReadOnly = false
    }.ConnectionString; // data source=test.sqlite;pooling=True;read only=False
File.Delete(DbFilePath);
using (SQLiteConnection conn = new SQLiteConnection(readWriteConnectionString))
using (SQLiteCommand cmd = new SQLiteCommand("CREATE TABLE items(id INTEGER NOT NULL PRIMARY KEY)", conn))
{
    conn.Open();
    cmd.ExecuteNonQuery();
}
while (true) // <= if we comment the loop, the program executes without error
{
    using (SQLiteConnection conn = new SQLiteConnection(readWriteConnectionString))
    using (SQLiteCommand cmd = new SQLiteCommand("INSERT INTO items(id) VALUES (1)", conn))
    {
        conn.Open();
        cmd.ExecuteNonQuery();
    }
    using (SQLiteConnection conn = new SQLiteConnection(readOnlyConnectionString))
    using (SQLiteCommand cmd = new SQLiteCommand("SELECT COUNT(*) FROM items", conn))
    {
        conn.Open();
        cmd.ExecuteScalar();
    }
    using (SQLiteConnection conn = new SQLiteConnection(readWriteConnectionString))
    using (SQLiteCommand cmd = new SQLiteCommand("DELETE FROM items", conn))
    {
        conn.Open();
        cmd.ExecuteNonQuery();
    }
}

1 个答案:

答案 0 :(得分:4)

SQLiteConnectionPool类的源代码看,它在管理池条目时只考虑文件名。

这是一个简短的摘录:

internal static void Add(
    string fileName,
    SQLiteConnectionHandle handle,
    int version

没有提到使用的连接字符串,只提到文件名。因此,当您需要不同的连接字符串以表现不同并且分开池时,使用内置连接池机制将无法工作。

现在,&#34;连接池&#34;因为概念是ADO.NET类层次结构的实现者决定他/她自己的东西。每个唯一连接字符串的SqlConnection类池,但IDbConnection的实现不需要这样做。

因此,这可能只是由System.Data.SQLite的创建者做出的设计决策。但是,我会给他们发一封电子邮件,询问这是否是故意的。