c# - 如何等待 SqlTransaction 提交而不是抛出 SqlException

标签 c# sql-server azure sqlexception sqltransaction

我正在尝试开发库存管理应用程序,目前我正在尝试对通过交易添加/删除项目进行概念验证。 该站点将托管在 Windows Azure 上,因此我必须使用 SqlTransation 来执行事务(无 DTC)。

这是我的 POC 库代码:

public static void AddQuantityByProductId(int argProductId, int argQuantity)
{
    UpdateQuantity(argProductId, argQuantity);
}

public static void ReduceQuantityByProductId(int argProductId, int argQuantity)
{
    UpdateQuantity(argProductId, argQuantity * -1);
}
private static void UpdateQuantity(int argProductId, int argQuantity)
{
        string conn = ConfigurationManager.ConnectionStrings["MyConn"].ConnectionString;
        using (SqlConnection connection = new SqlConnection(conn))
        {
            connection.Open();
            using (SqlTransaction transaction = 
                connection.BeginTransaction(IsolationLevel.Serializable))
            {
                SqlCommand command = connection.CreateCommand();
                command.Transaction = transaction;
                command.CommandTimeout = 30;

                command.CommandText = "select [Quantity] from StockItems where [Id] = '" + argProductId + "'";
                int quantity = (int)command.ExecuteScalar();
                int newQuantity = quantity + argQuantity;
                command.CommandText = "UPDATE StockItems SET [Quantity] = " + newQuantity + " WHERE [Id] = '" + argProductId + "'";
                command.ExecuteNonQuery();
                transaction.Commit();
            }
        }
}

我用来测试系统的代码:

static void Main(string[] args)
{
    int productId = 6; //hard coded Id , because the line already exists
    for (int i = 0; i < 10; i++)
    {
        Parallel.For(0, 10, (j) =>
        {
            StockItemDal.AddQuantityByProductId(productId, 10);
            StockItemDal.ReduceQuantityByProductId(productId, 10);

         });
    }
 }

异常(exception)情况: exception that is raised everytime

您能帮我找出问题所在吗?

最佳答案

问题有两个方面。

首先,没有办法等待死锁。用 SQL Server 术语来说,可以等待 block 结束。但死锁意味着多个查询正在等待其他查询释放对同一个数据*的独占保留,同时它们自己也持有其他数据上的锁,而其他查询也都在等待。如果所有人继续等待其他人放弃和释放,则什么也不会发生,并且他们将永远被锁定。 SQL Sever 检测到这一情况并强制终止并回滚除一个查询之外的所有查询。

这导致了第二个问题。

您正在强制执行可序列化 隔离级别事务,这是最严格的。在您的代码中,这种隔离级别的需求是可以理解的,因为您更新计数器并需要读取/递增/写入操作是原子的。但是,当对同一行数据的读写查询并行发生在两个不同的线程中时,就会出现问题。

这一切都导致了一个相当简单的解决方案。有一种比您正在使用的方法更好的方法来更新数量。可以在一条语句中完成所有操作,而不是离散读取、增量和写入 Sql 查询。

        command.CommandText = "UPDATE StockItems " + 
            "SET [Quantity] = [Quantity] + " + argQuantity + 
            " WHERE [Id] = '" + argProductId + "'";
        command.ExecuteNonQuery();
        transaction.Commit();

现在,由于这是单个语句,因此您不再需要事务。所有这些代码都可以消失。

* 数据不是很精确(故意)。事实上,有许多不同类型的 SQL Server 锁处于不同的粒度级别,它们一起工作以最大限度地减少对相同数据的争用并最大限度地提高性能,但这个细节对于答案来说并不重要。

关于c# - 如何等待 SqlTransaction 提交而不是抛出 SqlException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25405040/

相关文章:

c# - 阅读 RSS 时出错

c# - 使用 Dapper 对相关实体进行 CRUD

sql - 更新闭包表的最佳方法是什么?

azure - 追加到 CloudBlockBlob 流

c# - LINQ Count() until,这样效率更高吗?

c# - 如何在 C# 中使用投影/相机技术

sql - 删除重复的源到目的地(例如,对于 2 个不同的条目(行),德里到孟买和孟买到德里,输出应该是一个)

c# - 包含在创建查询后不起作用

azure - 通过 URL 将 IoT 消息发送到 Azure 事件中心

azure - 如何使用 Azure 认知搜索的知识挖掘结果并填充聊天机器人的知识库?