我不太了解 SQLite 的工作原理以及 EF 应如何与其正确通信,因此我不能说这是一个错误还是预期的行为,但无论如何,下面是发生错误的原因。
您看到该异常是因为 EF Core 尝试加载扩展两次或更多次。根据文档https://www.sqlite.org/loadext.html#implementation_details https://www.sqlite.org/loadext.html#implementation_details(参见 5. 持久可加载扩展):
可加载扩展的默认行为是,当最初调用的数据库连接关闭时,它会从进程内存中卸载。sqlite3_load_extension()
closes.
但我们也知道 EF Core 实现了连接池,这意味着使用相同的连接字符串,框架将尝试使用池中的连接,当您完成查询时,它会将连接返回到池中。这意味着它不卸载查询完成时的扩展只是因为连接未关闭。
最重要的是,只有物理连接被池化,但是一个对象SqliteConnection
类型是某种逻辑连接,实际上它是物理连接的抽象。
逻辑连接怎么样(SqliteConnection
)。实际上是一个新的实例SqliteConnection
是用每个创建的DbContext
您创建的实例,它完全由 EF Core 管理。 EF Core 似乎在对数据库的每个请求上打开/关闭此逻辑连接(note打开/关闭此连接是非常轻量级的操作,因为它不是物理连接,并且实际的物理连接是从池中获取的)。并且 EF Core 会在每次调用时发送请求来加载请求的扩展Open
.
那么让我们回到您的代码示例,看看发生了什么。
相同上下文的多个请求
using (var db = new Context())
{
var connection = (SqliteConnection)db.Database.GetDbConnection();
connecton.EnableExtensions(true);
conn.LoadExtension(@"SQLite Extensions\spellfix1.dll");
db.Models.FromSqlRaw(SOME QUERY).ToList();
db.Models.FromSqlRaw(SOMEQUERY).ToList(); // (1) will throw here
}
这会引发 (1),因为您只有一个实例SqliteConnection
并两次致电Open
方法(每个请求一个)发送加载扩展的请求。因此,第二次调用将尝试第二次加载扩展并抛出异常。
两个请求各有其自己的上下文
using (var db = new Context())
{
var connection = (SqliteConnection)db.Database.GetDbConnection();
connecton.EnableExtensions(true);
conn.LoadExtension(@"SQLite Extensions\spellfix1.dll");
db.Models.FromSqlRaw(SOME QUERY).ToList();
}
using (var db = new Context())
{
var connection = (SqliteConnection)db.Database.GetDbConnection();
connecton.EnableExtensions(true);
conn.LoadExtension(@"SQLite Extensions\spellfix1.dll");
db.Models.FromSqlRaw(SOME QUERY).ToList(); // (1) throws here
}
这会抛出(1)
因为物理连接用于加载第一个扩展using
仍然打开,这意味着在我们再次加载扩展之前尚未卸载它(1)
.