2

下面是对我正在处理的两个存储过程的两次调用的代码。我以为我已经设置为执行全部或全部原子事务,但是如果在第二个存储过程调用中出现错误,第一个仍然会执行。我仍然是用 C# 编写这些的新手,所以任何帮助将不胜感激。谢谢!

我还应该提到其他我觉得奇怪的事情:如果抛出异常并且程序进入“catch”块,它仍然会运行“finally”块中的代码。我的理解是,如果抛出异常,“catch”块中的代码将被执行。

编辑感谢下面的回复,我已经修复了 catch/finally 混淆并添加了一个 ExecuteNonQuery() 调用,因此也调用了第一个存储过程。但是,第一个存储过程在被调用时,需要先被调用并执行,然后第二个存储过程才能完成它的工作。这是可以在原子事务中完成的,还是必须单独运行?

try
{
    cm = Dts.Connections["serverName"];
    sqlConn = (SqlConnection)cm.AcquireConnection(Dts.Transaction);
    sqlTrans = sqlConn.BeginTransaction("QueueUpdates");                                


    if (dummyIndicator.Equals("Y"))
    {
        string temp = retrievedMessage.Substring(203, 17);
        int newNumber = (int)(long.Parse(temp) / 777);

        SqlParameter newNum = new SqlParameter("@newNum", num.Value);
        SqlParameter oldNum = new SqlParameter("@oldNum", newNumber);

        sqlComm = new SqlCommand("DB.dbo.sp_UpdateNumber", sqlConn, sqlTrans);
        sqlComm.CommandType = CommandType.StoredProcedure;
        sqlComm.Parameters.Add(newNum);
        sqlComm.Parameters.Add(oldNum);
        sqlComm.Transaction = sqlTrans;
        sqlComm.ExecuteNonQuery();

    }

    //Update records according to queue messages
    sqlComm = new SqlCommand("DB.dbo.sp_AgentIdAprCheck", sqlConn, sqlTrans);
    sqlComm.CommandType = CommandType.StoredProcedure;
    sqlComm.Parameters.Add(num);
    sqlComm.Parameters.Add(addOrUpdate);
    sqlComm.Parameters.Add(companyCode);
    sqlComm.Parameters.Add(agentID);
    sqlComm.Parameters.Add(firstName);
    sqlComm.Parameters.Add(lastName);
    sqlComm.Parameters.Add(suffix);
    sqlComm.Parameters.Add(taxIdType);
    sqlComm.Parameters.Add(entityType);
    sqlComm.Parameters.Add(corporateName);
    sqlComm.Parameters.Add(outNewNumber);
    sqlComm.Parameters.Add(outCurrentNumber);
    sqlComm.Parameters.Add(outOperator);
    sqlComm.Parameters.Add(outDate);
    sqlComm.Parameters.Add(returnVal);
    sqlComm.ExecuteNonQuery();

    sqlTrans.Commit();

    if (addOrUpd.Equals("ADD")){recordsAdded++;}
    else{recordsUpdated++;}

}
catch (Exception ex)
{
    _sqlDataErrors++;
    swLog.WriteLine("Message not updated: " + retrievedMessage);
    swLog.WriteLine("Error: " + ex.ToString());
    sqlTrans.Rollback();
}
finally
{                                
    cm.ReleaseConnection(sqlConn);
}
4

3 回答 3

1

首先,你为什么强调transaction原子性?Transaction意味着始终是原子的,任何事务都保证它的所有块都被提交或没有一个块被提交。

考虑在您的情况下使用事务 - 这取决于您的域逻辑。选择相当简单:

  • 如果可能只运行第一个 sp 而没有第二个 - 不要使用单个事务。
  • 如果不可能 - 使用它。

因此,如果您想进一步解释您的域规则和逻辑,我们可以说出最适合您的场景。

UPD 好的,在您稍微解释一下之后,我认为没有问题 - 当然您可以以特定方式实现它并获得预期的行为,以便来自您的两个 sp 的命令将由单个事务处理并作为一个事务处理。我还想指出代码的可能改进。

  1. 您可以使异常处理更加具体。现在,当出现任何问题时,您正在回滚。例如 - 如果Dts.Connections["serverName"];null,你会在sqlTrans初始化之前得到 NullReferenceException,然后在catch块中你会得到另一个NullReferenceException,处理sqlTrans,它是 null

  2. 您可以将代码拆分为特定于数据库的代码和其他所有代码,以确保该块中的异常始终是数据库层的异常。我的意思是做类似的事情

    string temp = retrievedMessage.Substring(203, 17);

    int newNumber = (int)(long.Parse(temp) / 777);

在其他地方,因为它具有潜在危险并且可能引发异常。

于 2012-08-27T06:34:44.217 回答
0
  1. 你似乎并没有真正执行第一个 SP,所以我猜第二个中的问题可能是因为这个?

  2. 的目的finally是始终执行,无论代码块如何结束(正常退出、异常、返回语句等都最终执行finally)。因此,它应该用于清理资源,而不是catch. 将Commit()调用移动到块结束之前的最后一行,并从块try中删除,然后事情应该开始按预期工作。ReleaseConnectioncatch

于 2012-08-27T00:13:36.357 回答
0

无论发生什么(无论是否发生异常)都将执行 finally 块,无论 try 块如何退出,控制总是传递给 finally 块。

将事务提交调用从 finally 块移到 ExecuteQuery 调用之后。此外,您正在 if 块中为第一个存储过程创建一个 sql 命令对象并将其保存在其中,sqlComm但在 if 块之外您正在为第二个存储过程创建另一个 sql 命令对象并将其保存在同一个sqlComm对象中。似乎您的第一个存储过程从未执行过。您可能必须更改那里的逻辑。

于 2012-08-27T00:14:02.753 回答