连接必须有效且打开才能在重试执行失败的查询时提交 sqllite 数据库的事务


public static void SetStatus( Status statusObject,int retryCount)
{
if (statusObject != null)
{
using (SqliteConnection dbConn = new SqliteConnection(dbURL))
{
IDbTransaction dbTransaction = null;
try
{
dbConn.Open();
dbTransaction = dbConn.BeginTransaction();
new SqliteCommand(some_query, dbConn).ExecuteNonQuery();
}
catch (Exception e)
{
dbTransaction.Rollback();
dbConn.Close();
if (retryCount > 0)
{
SetStatus(statusObject, --retryCount);
return;
}
else
throw e;
}
finally
{
try { dbTransaction.Commit(); }
catch (Exception e)
{
}
}
}
}
}

每当 ExecuteNonQuery 由于某些异常而失败时,我都有一个重试机制,它将再次运行相同的查询。在这种情况下,在第二次(重试时(出现以下异常-

"连接必须有效且开放才能提交事务">

问题基本上在于,您在finally块中提交事务(该块始终按设计运行(,即使您可能已经回滚了事务并关闭了catch块中的连接。

更简单的方法是在仍在try块中提交事务,因为毕竟,您只想在try中的所有代码都成功时提交事务。

话虽如此,您在那里与tryusing结构有一些奇怪的嵌套。你应该尽量保持像using这样的东西尽可能简洁。这样,您也不必自己处理关闭连接的问题。像这样:

public static void SetStatus(Status statusObject, int retryCount)
{
if (statusObject == null)
return;
try
{
using (var dbConn = new SqliteConnection(dbURL))
{
IDbTransaction dbTransaction = null;
try
{
dbConn.Open();
dbTransaction = dbConn.BeginTransaction();
new SqliteCommand(some_query, dbConn).ExecuteNonQuery();
dbTransaction.Commit();
}
catch
{
// transaction might be null if the connection couldn’t be opened
dbTransaction?.Rollback();
throw;
}
}
}
catch (Exception ex)
{
if (retryCount > 0)
SetStatus(statusObject, --retryCount);
else
throw ex;
}
}

最后一点,有人可能会争辩说,在这里绝对没有必要使用事务:您只在此数据库连接上执行单个查询,因此要么有效,要么失败,在这种情况下,无论如何都没有要回滚的内容。因此,除非您在那里有多个查询,否则您不需要显式事务(但我只是假设您只是将示例简化为仅显示单个查询(。

我的坏,我自己不恰当地使用"终于"搞砸了。因此,如果查询在第一次尝试时失败,它将转到 catch 块,这将触发重新运行查询的调用。第二次查询执行将成功并提交事务。然后,控件进入第一个查询的最终位置,该查询将再次尝试提交已回滚的事务。因此,它会抛出该异常。 因此,如果将提交移动到主尝试块解决了问题。

最新更新