TransactionScope не откатывает транзакцию - PullRequest
11 голосов
/ 04 декабря 2008

Вот текущая архитектура моего исходного кода области транзакции. Третья вставка генерирует исключение .NET (не исключение SQL) и не откатывает два предыдущих оператора вставки. Что я делаю не так?

РЕДАКТИРОВАТЬ: Я удалил try / catch из insert2 и insert3. Я также удалил утилиту обработки исключений из вставки try / catch и поставил «throw ex». По-прежнему не выполняется откат транзакции.

РЕДАКТИРОВАТЬ 2: Я добавил try / catch обратно к методу Insert3 и просто добавил «throw» в оператор catch. По-прежнему не выполняется откат транзакции.

ОБНОВЛЕНИЕ: На основании полученных отзывов класс "SqlHelper" использует объект SqlConnection для установления соединения с базой данных, затем создает объект SqlCommand, задает для свойства CommandType значение "StoredProcedure" и вызывает метод ExecuteNonQuery SqlCommand.

Я также не добавил Transaction Binding = Explicit Unbind к текущей строке соединения. Я добавлю это во время моего следующего теста.

public void InsertStuff()
{
    try
    {
        using(TransactionScope ts = new TransactionScope())
        {
            //perform insert 1
            using(SqlHelper sh = new SqlHelper())
            {
                SqlParameter[] sp = { /* create parameters for first insert */ };

                sh.Insert("MyInsert1", sp);
            }

            //perform insert 2
            this.Insert2();

            //perform insert 3 - breaks here!!!!!
            this.Insert3();

            ts.Complete();            
        }
    }
    catch(Exception ex)
    {
        throw ex;
    }
}

public void Insert2()
{
    //perform insert 2
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /* create parameters for second insert */ };

        sh.Insert("MyInsert2", sp);
    }
}

public void Insert3()
{
    //perform insert 3
    using(SqlHelper sh = new SqlHelper())
    {
        SqlParameter[] sp = { /*create parameters for third insert */ };

        sh.Insert("MyInsert3", sp);
    }
}

Ответы [ 5 ]

25 голосов
/ 05 марта 2009

Я также столкнулся с подобной проблемой. Моя проблема возникла из-за того, что SqlConnection, который я использовал в моих SqlCommands, уже был открыт до создания TransactionScope, поэтому он никогда не был зачислен в TransactionScope как транзакция.

Возможно ли, что класс SqlHelper повторно использует экземпляр SqlConnection, который открыт перед входом в ваш блок TransactionScope?

6 голосов
/ 04 декабря 2008

Похоже, что вы перехватываете исключение в Insert3 (), поэтому ваш код продолжается после вызова. Если вы хотите выполнить откат, вам нужно, чтобы исключение всплыло в блоке try / catch в основной подпрограмме, чтобы оператор ts.Complete () никогда не вызывался.

1 голос
/ 04 декабря 2008

(на основании отредактированной версии, которая не поглощает исключения)

Сколько времени занимает операция? Если какой-либо из них работает очень долго, возможно, вас укусила функция Привязка транзакций ошибка , т. Е. Соединение оборвалось. Попробуйте добавить Transaction Binding=Explicit Unbind в строку подключения.

1 голос
/ 04 декабря 2008

Неявный откат произойдет, только если использование завершено без вызова ts.complete. Поскольку вы обрабатываете исключение в Insert3 (), оно никогда не приводит к завершению оператора using.

Либо повторно выдать исключение, либо уведомить вызывающего абонента о необходимости отката (изменить подпись Insert3 () на bool Insert3 ()?)

0 голосов
/ 18 апреля 2012

Я не вижу ваш вспомогательный класс, но откат области действия транзакции, если вы не вызываете полный оператор, даже если вы получаете ошибку из кода .NET. Я скопировал один пример для вас. Возможно, вы делаете что-то не так при отладке. В этом примере ошибка в коде .net и блоке захвата, аналогичном вашему.

  private static readonly string _connectionString = ConnectionString.GetDbConnection();

    private const string inserttStr = @"INSERT INTO dbo.testTable (col1) VALUES(@test);";

        /// <summary>
        /// Execute command on DBMS.
        /// </summary>
        /// <param name="command">Command to execute.</param>
        private void ExecuteNonQuery(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException("Parameter 'command' can't be null!");

            using (IDbConnection connection = new SqlConnection(_connectionString))
            {
                command.Connection = connection;
                connection.Open();
                command.ExecuteNonQuery();
            }
        }

        public void FirstMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello1"));


                ExecuteNonQuery(command);

        }

        public void SecondMethod()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello2"));


                ExecuteNonQuery(command);

        }

        public void ThirdMethodCauseNetException()
        {
            IDbCommand command = new SqlCommand(inserttStr);
            command.Parameters.Add(new SqlParameter("@test", "Hello3"));


                ExecuteNonQuery(command);
            int a = 0;
            int b = 1/a;

        }

    public void MainWrap()
    {


        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
        //TransactionScopeOption.Required, tso
        try
        {
            using (TransactionScope sc = new TransactionScope())
            {
                FirstMethod();
                SecondMethod();
                ThirdMethodCauseNetException();
                sc.Complete();
            }
        }
        catch (Exception ex)
        {
            logger.ErrorException("eee ",ex);

        }
    }

Если вы хотите отлаживать транзакции, вы можете использовать этот скрипт для просмотра блокировок, статуса ожидания и т. Д.

SELECT 
request_session_id AS spid,
CASE transaction_isolation_level 
WHEN 0 THEN 'Unspecified' 
WHEN 1 THEN 'ReadUncomitted' 
WHEN 2 THEN 'Readcomitted' 
WHEN 3 THEN 'Repeatable' 
WHEN 4 THEN 'Serializable' 
WHEN 5 THEN 'Snapshot' END AS TRANSACTION_ISOLATION_LEVEL ,
resource_type AS restype,
resource_database_id AS dbid,
DB_NAME(resource_database_id) as DBNAME,
resource_description AS res,
resource_associated_entity_id AS resid,
CASE 
when resource_type = 'OBJECT' then OBJECT_NAME( resource_associated_entity_id) 
ELSE 'N/A'
END as ObjectName,
request_mode AS mode,
request_status AS status
FROM sys.dm_tran_locks l
left join sys.dm_exec_sessions s on l.request_session_id = s.session_id
where resource_database_id = 24
order by spid, restype, dbname;

Перед вызовом метода исключения вы увидите один SPID для двух вызовов метода.

two calls before exception

Уровень изоляции по умолчанию является сериализуемым. Подробнее о блокировках и транзакциях можно прочитать здесь

...