简体   繁体   English

将Massive与SQLite一起使用时,“数据库被锁定”异常

[英]“Database is locked” exception when using Massive with SQLite

Recently I browse the micro ORMs and I like Massive for SQLite because its simple. 最近我浏览了微型ORM,我喜欢Massive for SQLite,因为它很简单。 But I have now an issue. 但我现在有一个问题。

I am just running some select statement followed by an update statement but I am getting an exception. 我只是运行一些select语句后跟一个更新语句,但我得到一个例外。 Below is my code : 以下是我的代码:

 var tbl = new Cust();
            var customers = tbl.All(where: "CustomerID > @0", orderBy: "FirstName", columns: "CustomerID,FirstName", args: 4);
            var firstCustomerName= customers.First().FirstName;

            var c = tbl.Update(new { FirstName = "Updated2" }, 4); //Exception is here!

            //Same happens even when using another object
            //var tbl2 = new Cust();
            //tbl2.Update(new { FirstName = "UpdatedName" }, 4);//Exception is here!

The Exception Message is : "Database is locked", at a method below in the Massive.SQLite source 异常消息是:“数据库被锁定”,在Massive.SQLite源中的下面的方法中

public virtual int Execute(IEnumerable<DbCommand> commands)
{
       var result = 0;
            using (var conn = OpenConnection())
            {
                using (var tx = conn.BeginTransaction())
                {
                    foreach (var cmd in commands)
                    {
                        cmd.Connection = conn;
                        cmd.Transaction = tx;
                        result += cmd.ExecuteNonQuery();
                    }
                    tx.Commit();//Here is the Exception!
                }
            }
            return result;     
}

When I look at Massive.SQLite source i see that massive never closes connections but instead relays on the using statement to dispose the connection object, as you can see in the above code. 当我查看Massive.SQLite源代码时,我看到大量从不关闭连接,而是继续使用using语句来处理连接对象,如上面的代码所示。

OpenConnection() in the above code is a method that returns a new connection every time called. 上面代码中的OpenConnection()是一种每次调用时都返回一个新连接的方法。

 public virtual DbConnection OpenConnection()
 {
            var result = _factory.CreateConnection();
            result.ConnectionString = ConnectionString;
            result.Open();
            return result;
 }

If the case is that Massive is not closing the connection, and according to this SO question Sqlite is not good at concurrent connections and I am supposed to close it, how can I close it then? 如果案例是Massive没有关闭连接,并根据这个SO问题 Sqlite不擅长并发连接,我应该关闭它,我怎么能关闭它呢? - the connection is not exposed to me. - 连接不会暴露给我。

I want to hear the best practice from developers using Massive with SQLite. 我想听听开发人员使用Massive和SQLite的最佳实践。

SQlite likes to have a single opened connection. SQlite喜欢打开一个连接。

Massive is managing the connections correctly however it leaves the ExecuteReader "open" in the Query method which can cause troubles: Massive正确管理连接,但它在Query方法中将ExecuteReader “open”,这可能会导致麻烦:

Robert Simpson wrote: 罗伯特辛普森写道:

Leaving readers open could cause issues. 让读者保持开放可能会导致问题。 Those won't get cleaned up until the lazy garbage collector gets around to it. 在懒惰的垃圾收集器到达它之前,这些将不会被清理干净。 It'd certainly be better in any case to have using() statements around your readers at the very least. 在任何情况下,至少在读者周围使用using()语句肯定会更好。 The following objects use unmanaged resources that the garbage collector will be lazy about cleaning up: 以下对象使用垃圾收集器将在清理时懒惰的非托管资源:

SQLiteCommand, SQLiteConnection, SQLiteDataReader, and possibly SQLiteTransaction if I recall correctly. 如果我没记错的话,SQLiteCommand,SQLiteConnection,SQLiteDataReader以及可能的SQLiteTransaction。

So put an using around the ExecuteReader() in the Query method and it should work fine: 因此,在Query方法中using ExecuteReader() ,它应该可以正常工作:

public virtual IEnumerable<dynamic> Query(string sql, params object[] args)
{
    using (var conn = OpenConnection())
    {
        using (var rdr = CreateCommand(sql, conn, args).ExecuteReader())
        {
            while (rdr.Read())
            {
                yield return rdr.RecordToExpando(); ;
            }
        }
    }
}

Some notes and other workarounds which not requires changing the Massive source: 一些注释和其他解决方法不需要更改Massive源:

  • you can enable connection pooling in SQLite with Pooling setting: 您可以在SQLite中使用Pooling设置启用连接池:

     connectionString="Data Source=test.db;Version=3;Pooling=True;Max Pool Size=100;" 
  • the Query usually works right if it the reads all the data from the reader. 如果从读取器读取所有数据, Query通常正常工作。 But you used First() which combined with the yield return left the reader open. 但是你使用了First() ,它结合了yield return让读者打开了。 So if you evaluate the query with ToArray() it will also work: 因此,如果您使用ToArray()评估查询,它也将起作用:

     var firstCustomerName= customers.ToArray().First().FirstName; 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM