문제

Recently I browse the micro ORMs and I like Massive for SQLite because its simple. But I have now an issue.

I am just running some select statement followed by an update statement but I am getting an exception. 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

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.

OpenConnection() in the above code is a method that returns a new connection every time called.

 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? - the connection is not exposed to me.

I want to hear the best practice from developers using Massive with SQLite.

도움이 되었습니까?

해결책

SQlite likes to have a single opened connection.

Massive is managing the connections correctly however it leaves the ExecuteReader "open" in the Query method which can cause troubles:

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. 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.

So put an using around the ExecuteReader() in the Query method and it should work fine:

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:

  • you can enable connection pooling in SQLite with Pooling setting:

    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. But you used First() which combined with the yield return left the reader open. So if you evaluate the query with ToArray() it will also work:

    var firstCustomerName= customers.ToArray().First().FirstName;
    
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top