Почему неудачная транзакция не откатывается с помощью TransactionScope?

Я пытаюсь достичь следующего: я вызываю хранимые процедуры из двух разных баз данных. У SP есть простая вставка в таблицу. Нет проблем, если обе транзакции прошли успешно, но когда я попытался сгенерировать исключение во 2-м SP DB, первый не откатился. Что я здесь делаю не так?

Код C #:

using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress))
{
    // Update DB1
    using (SqlConnection con = new SqlConnection(connectionString1))
    {
        con.Open();
        SqlCommand command1 = new SqlCommand("sp_1", con);
        command1.ExecuteNonQuery();

        // Update DB2 
        using (SqlConnection con2 = new SqlConnection(connectionString2))
        {
            con2.Open();
            SqlCommand command2 = new SqlCommand("sp_2", con2);
            command2.ExecuteNonQuery();
        }
    }
    scope.Complete();
}

DB1 SP :

BEGIN
    INSERT INTO TABLE_X1 VALUES(...)
END

DB2 SP :

BEGIN
    THROW 51000, 'The record does not exist.', 1; 

    INSERT INTO TABLE_X2 VALUES(...)
END

person Code Wines    schedule 09.08.2020    source источник
comment
Транзакции определяются подключением. Когда вы включаете несколько подключений в транзакцию, вы включаете несколько подключений к базе данных, если строки подключения различны. При использовании нескольких подключений транзакция перерастет в распределенную транзакцию, и в этом случае вам необходимо убедиться, что ваш сервер поддерживает распределенные транзакции.   -  person Ross Bush    schedule 09.08.2020


Ответы (2)


Вам необходимо использовать опцию TransactionScopeOption.Required, чтобы разрешить транзакцию.

Если обе базы данных находятся на одном сервере SQL, используйте одно и то же соединение, чтобы избежать запуска распределенных транзакций (если строка подключения точно такая же, этого не должно происходить).

TransactionScope: предотвращение распределенных транзакций

Если вы хотите использовать транзакцию и если базы данных находятся на разных серверах (или в разных экземплярах на одном сервере), то нет способа избежать распределенных транзакций. В этом случае включите MSDTC, подробности см. Здесь.

https://www.dbrnd.com/2016/11/sql-server-how-to-configure-and-enable-msdtc-microsoft-distributed-transaction-coordinator/

person K4M    schedule 09.08.2020

Указывая TransactionScopeOption.Suppress в качестве аргумента конструктора для TransactionScope, вы выбираете не иметь никаких транзакций.

Если область создается с помощью Подавить, он никогда не участвует в транзакции, независимо от того, присутствует ли внешняя транзакция. Экземпляр области видимости с этим значением всегда имеет [sic] null в качестве внешней транзакции.

https://docs.microsoft.com/en-us/dotnet/framework/data/transactions/implementing-an-implicit-transaction-using-transaction-scope

Предположительно, этот параметр предназначен для случаев, когда вы знаете, что кто-то другой мог начать транзакцию до вызова вашего метода, но вы знаете, что то, что вы делаете в своем методе, должно выполняться вне транзакции.

Поскольку похоже, что вы хотите убедиться, что транзакция откатывается, вы, вероятно, захотите использовать Required (по умолчанию, если вы опустите аргумент) или, возможно, RequiresNew.

person StriplingWarrior    schedule 09.08.2020
comment
Если я попытаюсь указать в аргументе Required или RequiresNew, я получаю исключение, в котором говорится: «Эта платформа не поддерживает распределенные транзакции». - person Code Wines; 09.08.2020
comment
проверьте здесь, как можно справиться с распределенными транзакциями или избежать их stackoverflow.com/ questions / 3187632 / - person K4M; 09.08.2020