池化的只读 SQLite 连接用于读写访问
Pooled read-only SQLite connection is used for read-write access
使用的 nuget 包:System.Data.SQLite.Core、1.0.98.1
问题:在我的程序中,我在某些情况下使用启用了池和只读访问的 SQLite。通常它工作正常,但如果有很多混合 read-only/read-write 对数据库的密集请求,则程序失败并出现以下异常:
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();
}
}
从 SQLiteConnectionPool class 的源代码来看,它似乎在管理池条目时只考虑文件名。
这里有一段简短的摘录:
internal static void Add(
string fileName,
SQLiteConnectionHandle handle,
int version
没有提到使用的连接字符串,只有文件名。因此,当您需要不同的连接字符串表现不同并且单独池化时,使用内置的连接池机制将不起作用。
现在,"connection pooling" 作为概念是由 ADO.NET class 层次结构的实现者决定的 him/herself。 SqlConnection classes 池每个唯一的连接字符串,但这绝不是 IDbConnection 的实现所必需的。
因此,这可能只是 System.Data.SQLite 的创建者做出的设计决定。但是,我会向他们发送电子邮件,询问这是否是故意的。
使用的 nuget 包:System.Data.SQLite.Core、1.0.98.1
问题:在我的程序中,我在某些情况下使用启用了池和只读访问的 SQLite。通常它工作正常,但如果有很多混合 read-only/read-write 对数据库的密集请求,则程序失败并出现以下异常:
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();
}
}
从 SQLiteConnectionPool class 的源代码来看,它似乎在管理池条目时只考虑文件名。
这里有一段简短的摘录:
internal static void Add(
string fileName,
SQLiteConnectionHandle handle,
int version
没有提到使用的连接字符串,只有文件名。因此,当您需要不同的连接字符串表现不同并且单独池化时,使用内置的连接池机制将不起作用。
现在,"connection pooling" 作为概念是由 ADO.NET class 层次结构的实现者决定的 him/herself。 SqlConnection classes 池每个唯一的连接字符串,但这绝不是 IDbConnection 的实现所必需的。
因此,这可能只是 System.Data.SQLite 的创建者做出的设计决定。但是,我会向他们发送电子邮件,询问这是否是故意的。