IP更改后重新连接已删除的SqlConnection而不回滚事务

时间:2011-12-11 21:41:49

标签: .net sql sql-server tsql

我有多个客户端通过不可靠(无线/ gprs)网络连接到SQL Server,并在几分钟内执行大量小型查询和插入。如果在此过程中网络连接中断,则会回滚整个事务并需要重新启动。由于业务需求,流程必须是事务性的(即其他客户端看到来自其他客户端的完整数据集或根本看不到它。)

我希望能够检测连接何时中断并能够重新连接到SQL Server并继续处理刚刚删除的同一事务中的,并避免从一开始就重新启动。目前我在打开连接后立即使用sp_getbindtoken,将CommandTimeout设置为较小的值(远小于TCP KeepAlive),如果在ExecuteNonQuery期间出现超时,则打开与服务器的新连接,从进程开始使用令牌调用sp_bindsession。然后我继续使用绑定到先前进程'transaction'的会话的新连接进行处理。

到目前为止它的工作几乎完美,但根据MSDN,此API已弃用,将在SQL Server的未来版本中删除。问题是:如果没有这两个命令,我怎样才能获得相同的结果?有没有其他方法可以从丢弃的TCP连接中恢复事务?

编辑/更多信息:客户端应用程序在带有条形码扫描程序的Windows CE设备上运行。我提供设备和软件,所以我可以随意放置任何我需要的东西。数据库由第三方托管在受保护的环境中,我和客户都无法控制它。我总共要发送约50MB的每日销售数据。我可以使用SP来保存数据,但它仍然必须传输,并且一个大的参数对SP的一次调用在GPRS / EDGE链接上成功率接近0%。

由于整个解决方案在生产环境中工作,我希望将更改保持在最低限度。与sp_bindsession具有相同语义的替代API将是完美的。

2 个答案:

答案 0 :(得分:2)

我只是不买那个〜50MB的每日销售数据需要在一次交易中。我买单个sales-tansactions需要包含在sql-transaction中,但那些更像是每个1K。您确定无法在存储过程中在服务器上运行多个小事务吗?如果它必须是来自每个设备的全部或全部,则通过小事务将设备加载到临时表。完成设备后,在事务中使用服务器上的存储过程来刷新登台表。或者只是在完成上传时放置一个布尔列,然后在上传完成时在单个更新中翻转该标志。一个50MB的交易真的会破坏交易日志并锁定其他更新。

答案 1 :(得分:0)

您提供的MSDN链接建议使用MARS。根据文章:

  

MARS允许连接用于具有多个挂起操作的读取操作和数据操作语言(DML)操作。此功能消除了应用程序处理连接繁忙错误的需要。此外,MARS可以替换服务器端游标的用户,这通常会消耗更多资源。最后,因为多个操作可以在单个连接上运行,所以它们可以共享相同的事务上下文,从而无需使用sp_getbindtoken和sp_bindsession系统存储过程。

这样您就可以使用BeginTrasaction,除非您明确提交,否则事务将自动回滚。您可以捕获commit语句的失败并递归尝试重新提交它,直到事务的提交成功返回。只是一个想法。也许是这样的:

private static void ExecuteSqlTransaction(string connectionString)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();

        SqlCommand command = connection.CreateCommand();
        SqlTransaction transaction;

        // Start a local transaction.
        transaction = connection.BeginTransaction("SampleTransaction");

        // Must assign both transaction object and connection
        // to Command object for a pending local transaction
        command.Connection = connection;
        command.Transaction = transaction;

        try
        {
            command.CommandText =
                "Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
            command.ExecuteNonQuery();
            command.CommandText =
                "Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
            command.ExecuteNonQuery();

            // Attempt to commit the transaction.
            transaction.Commit();
            //Both records are written to database.
        }
        catch (Exception ex)
        {
            // Attempt to roll back the transaction.
            try
            {
                transaction.Rollback();
            }
            catch (Exception ex2)
            {
                // This catch block will handle any errors that may have occurred
                // on the server that would cause the rollback to fail, such as
                // a closed connection.
                ExecuteSqlTransaction(connectionString);
            }
        }
    }
}