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由于某些异常而失败时,我都会有一个重试机制,它将再次运行相同的查询。在这种情况下,第二次(重试时)会出现以下异常 -
"连接必须有效并且打开以提交事务"
答案 0 :(得分:2)
问题基本上是,您在finally
块中提交事务 - 它始终按设计运行 - 即使您可能已经回滚了事务并关闭了catch
块中的连接
更简单的方法是在try
块中提交事务,因为毕竟,如果try
中的所有代码都成功,您只想提交事务。
话虽如此,你正在使用try
和using
构造进行奇怪的嵌套。您应尽量使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;
}
}
最后一点,人们可能会争辩说,在这里使用事务绝对没有必要:你只是在这个数据库连接上执行一个查询,所以要么工作,要么失败,在这种情况下没有回滚的东西无论如何。因此,除非您在那里有多个查询,否则您不需要显式事务(但我只是假设您只是简化了示例以显示单个查询)。
答案 1 :(得分:0)
我的坏,搞砸了“最后”不正确地使用自己。所以如果查询在第一次尝试时失败,它将进入catch块,这将触发调用以重新运行查询。第二次查询执行将成功并且事务将然后,控件转到第一个查询的最后一个查询,它将再次尝试提交已经回滚的事务。所以,它会引发异常。 因此,如果将提交移动到主try块,则修复了问题。