1

尝试使用带有 SubSonic 和 SQLite 的事务时,我遇到了锁定异常。我从一个线程中使用它,并且没有其他进程访问我的数据库,所以我真的没想到会出现任何此类问题。

如果我在下面编写这样的代码,我会在循环内第二次调用 Save() 时遇到异常 - 所以第三次调用 Save() 。

       using (TransactionScope ts = new TransactionScope())
       {
            using (SharedDbConnectionScope sharedConnectinScope = new SharedDbConnectionScope())
            { 
                SomeDALObject x = new SomeDALObject()
                x.Property1 = "blah";
                x.Property2 = "blah blah";
                x.Save();

                foreach (KeyValuePair<string, string> attribute in attributes)
                { 
                    AnotherDALObject y = new AnotherDALObject()
                    y.Property1 = attribute.Key
                    y.Property2 = attribute.Value
               
                    y.Save();  // this is where the exception is raised, on the 2nd time through this loop
                }
            }
       }

如果我有上面的 using() 语句,或者如果我有,using (TransactionScope ts = new TransactionScope())那么我会收到一条System.Data.SQLite.SQLiteExceptionwith 消息

数据库文件被锁定

数据库被锁定

堆栈跟踪是:

   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()
   at System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock)
   at System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel)
   at System.Data.SQLite.SQLiteConnection.BeginTransaction()
   at System.Data.SQLite.SQLiteEnlistment..ctor(SQLiteConnection cnn, Transaction scope)
   at System.Data.SQLite.SQLiteConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SQLite.SQLiteConnection.Open()
   at SubSonic.SQLiteDataProvider.CreateConnection(String newConnectionString)
   at SubSonic.SQLiteDataProvider.CreateConnection()
   at SubSonic.SQLiteDataProvider.ExecuteScalar(QueryCommand qry)
   at SubSonic.DataService.ExecuteScalar(QueryCommand cmd)
   at SubSonic.ActiveRecord`1.Save(String userName)
   at SubSonic.ActiveRecord`1.Save()
   at (my line of code above).

如果我将 using 语句以相反的方式嵌套,SharedDbConnectionScope 在外部,那么我会收到一条TransactionException 带有消息“操作对事务状态无效”。堆栈跟踪是:

at System.Transactions.TransactionState.EnlistVolatile(InternalTransaction tx, IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistVolatile(IEnlistmentNotification enlistmentNotification, EnlistmentOptions enlistmentOptions)
   at System.Data.SQLite.SQLiteEnlistment..ctor(SQLiteConnection cnn, Transaction scope)
   at System.Data.SQLite.SQLiteConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SQLite.SQLiteConnection.Open()
   at SubSonic.SQLiteDataProvider.CreateConnection(String newConnectionString)
   at SubSonic.SQLiteDataProvider.CreateConnection()
   at SubSonic.SQLiteDataProvider.ExecuteScalar(QueryCommand qry)
   at SubSonic.DataService.ExecuteScalar(QueryCommand cmd)
   at SubSonic.ActiveRecord`1.Save(String userName)
   at SubSonic.ActiveRecord`1.Save()
   at (my line of code above)

内部异常是“事务超时”

我生成的 DAL 类中没有任何自定义代码,或者我能想到的任何其他聪明的东西都会导致这种情况。

其他人遇到过这样的交易问题,或者有人可以建议我从哪里开始寻找问题?

谢谢!

更新:我注意到版本 1.0.61-65(例如此处)的发行说明中提到了与事务相关的内容,因此更新 SubSonic 以使用最新版本的 .Net 数据提供程序可能会解决其中一些问题。 .

4

3 回答 3

4

在为 subsonic 2.x 做修订的 sqlite 提供程序时,我基于现有的 subsonic sqlserver 测试创建了一整套单元测试。(这些测试也使用修改后的代码进行了检查。)唯一失败的测试是与事务相关的测试(可能也是迁移测试)。如您所见,“数据库文件已锁定”错误消息。Subsonic 主要是为 sql server 编写的,它不像 SQLIte 那样做文件级锁定,所以有些东西不起作用;它需要重写以更好地处理这个问题。

我从来没有像你一样使用过 TransactionScope。我像这样进行亚音速 2.2 事务,到目前为止 SQLite 提供程序没有问题。如果处理多行或者它真的很慢,我可以确认你需要使用 SQLite 的事务。

public void DeleteStuff(List<Stuff> piaRemoves)
{
    QueryCommandCollection qcc = new QueryCommandCollection();

    foreach(Stuff item in piaRemoves)
    {
        Query qry1 = new Query(Stuff.Schema);
        qry1.QueryType = QueryType.Delete;
        qry1.AddWhere(Stuff.Columns.ItemID, item.ItemID);
        qry1.AddWhere(Stuff.Columns.ColumnID, item.ColumnID);
        qry1.AddWhere(Stuff.Columns.ParentID, item.ParentID);
        QueryCommand cmd = qry1.BuildDeleteCommand();
        qcc.Add(cmd);
    }
    DataService.ExecuteTransaction(qcc);
}
于 2010-02-19T00:39:03.147 回答
2

我最终使用了 Paul 的建议,并将我的代码改写成这样:

    QueryCommandCollection qcc = new QueryCommandCollection();

    SomeDALObject x = new SomeDALObject()
    x.Property1 = "blah";
    x.Property2 = "blah blah";
    qcc.Add(x.GetSaveCommand());

    foreach (KeyValuePair<string, string> attribute in attributes)
    { 
        AnotherDALObject y = new AnotherDALObject()
        y.Property1 = attribute.Key
        y.Property2 = attribute.Value

        qcc.Add(y.GetSaveCommand());
    }

    DataService.ExecuteTransaction(qcc);

这实际上要好得多,因为数据库命中的所有准备工作都是在事务打开之前完成的,因此事务打开的时间要少得多。

如果您需要取回自动生成的 ID 以便为子记录运行 INSERT,这将无法正常工作;您需要为此使用不同的方法。

然后我遇到了其他一些线程/事务问题:当我有多个线程同时执行 DataService.ExecuteTransaction() 时,我会得到 AccessViolationExceptions 和 NullReferenceExceptions,基本上有点乱。但是更改为使用Paul 的SubSonic 分支和更新的 SQLDataProvider 并更改为使用System.Data.SQLite v1.0.65.0似乎立即修复了它。万岁!

更新:实际上我在使用带有 sqlite 的 SubSonic 时仍然遇到线程问题。基本上,SubSonic 中的 SQLiteDataProvider 并不是为了处理多线程而编写的。更多来...

于 2010-02-19T19:25:30.557 回答
0

我们正在使用 SQL lite 来测试与数据库操作相关的代码。SQL lite 不支持嵌套事务。我们在 NHibernate 和 .Net 事务中遇到了类似的问题。最终我们不得不使用 SQL Express 来测试与数据库相关的代码。

于 2010-02-18T23:25:46.037 回答