Ошибка вставки вложенных транзакций

Я использую вложенные транзакции, используя интерфейс IDBConecction на С#. У меня есть методы, которые вставляют данные в две разные таблицы, но когда дело доходит до второй вставки, первая транзакция вставки блокирует вторую, вызывая исключение тайм-аута.

public void FirstInsert()
{
    using (var cn = new Connection().GetConnection())
    {
        cn.Open();
        using (var tran = cn.BeginTransaction())
        {
            try
            {
                //1st insert
                SecondInsert() //calling second insert method
                tran.Commit();                
            }
            catch
            {
                tran.Rollback();
            }
        }
    }
}

public void SecondInsert()
{
    using (var cn = new Connection().GetConnection())
    {
        cn.Open();
        using (var tran = cn.BeginTransaction())
        {
            try
            {
                //2nd insert, this one fails
                tran.Commit();                
            }
            catch
            {
                tran.Rollback();
            }
        }
    }
}

Когда я проверяю на SqlServer, первая вставка имеет SPID 56, затем, когда вторая вставка выполняется с SPID 57, и я использую

exec sp_who2

В столбце «BlkBy» для SPID 57 написано, что он заблокирован SPID 56.

Как я могу преодолеть эти проблемы?


person jecarfor    schedule 27.06.2015    source источник
comment
Можете ли вы показать строки подключения и вставки? Вставки в две разные таблицы не должны блокировать друг друга, и обычно два идентичных подключения от одного и того же клиента будут совместно использовать распределенную транзакцию, но это можно отключить в строке подключения. Нет смысла открывать два соединения на основе примера кода, но я полагаю, что у вас есть причина для этого. Использование параметра строки подключения MARS (несколько активных наборов результатов) может позволить вам получать результаты из двух запросов параллельно, но из вашего описания вам это не нужно.   -  person Rattle    schedule 28.06.2015
comment
@Rattle небольшое исправление: два объекта SqlConnection никогда не используют одну и ту же транзакцию. Даже если он распределен, он по-прежнему отображается как два сеанса для SQL Server.   -  person usr    schedule 28.06.2015


Ответы (2)


Используйте одно соединение для обеих операций. Вероятно, это связано с передачей объекта соединения.

Обычно шаблон соединение + транзакция на запрос хорошо решает эту проблему. Открытие соединения во всех видах методов - это запах кода. Это показывает, что инфраструктура не справляется с этим.

person usr    schedule 27.06.2015

Вы делаете правильно, но в вашем втором методе нет необходимости в отдельном объекте соединения и объекте транзакции, поскольку вызов secondinsert() уже находится внутри области транзакции. Ваш код может быть просто

public void FirstInsert(){
    using (var cn = new Connection().GetConnection()){
        cn.Open();
        using (var tran = cn.BeginTransaction()){
            try{
                //1st insert
                SecondInsert() //calling second insert method
                tran.Commit();                
            }
            catch{
                tran.Rollback();
            }
        }
    }
}

public void SecondInsert(){

               //perform second insert operation             
        }
    }
}
person Rahul    schedule 27.06.2015
comment
Мне нужна транзакция и соединение в каждом методе, потому что мне также нужно вызвать 2-й метод из других методов, кроме метода из примера. В основном мне нужно, чтобы каждая фиксируемая транзакция была в отдельном методе, чтобы избежать повторения, а также чтобы эти методы вызывались везде. - person jecarfor; 28.06.2015
comment
@jecarfor, в этом случае рассмотрите возможность передачи того же объекта соединения/транса вашему второму методу и используйте его. - person Rahul; 28.06.2015
comment
Но если я вызову .Commit() из второго метода, будет ли зафиксирована вся транзакция? Это потому, что после второй вставки мне может понадобиться вызвать другую вставку, и, как я уже упоминал ранее, мне нужно, чтобы почти каждая вставка/обновление было в отдельном методе на тот случай, если мне нужно только вызвать его из другого места, например, вызов только 2-я вставка. Я хочу иметь что-то похожее на использование атрибута [Autocomplete(true)] в WCF, но без WCF, который с помощью правильной привязки отслеживает все транзакции до тех пор, пока вызов службы не завершится без исключения ошибки. - person jecarfor; 29.06.2015