TransactionScope и уровень изоляции - PullRequest
14 голосов
/ 06 апреля 2009

у нас проблема с использованием TransactionScope. TransactionScope дает нам очень хорошую гибкость в использовании транзакций на нашем уровне доступа к данным. На этом пути мы можем использовать транзакции неявные или явные. Некоторое повышение производительности снова происходит в транзакциях ADO.NET, но в настоящее время это не является проблемой. Однако у нас есть проблемы с блокировкой. В приведенном ниже примере кода, хотя уровень изоляции установлен на ReadCommitted, невозможно выполнить инструкцию Select SQL из другого клиента в таблице testTable, пока не будет зафиксирована основная транзакция (в методе Main), поскольку существует блокировка всей таблицы. Мы также пытались использовать только одно соединение для всех методов, но одинаковое поведение. Наша СУБД - SQL Server 2008. Есть что-то, чего мы не поняли?

С уважением Антон Калчик

См. Этот пример кода:

class Program
{
    public class DAL
    {
        private const string _connectionString = @"Data Source=localhost\fsdf;Initial Catalog=fasdfsa;Integrated Security=SSPI;";

        private const string inserttStr = @"INSERT INTO dbo.testTable (test) 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"));

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }

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

            using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required))
            {
                ExecuteNonQuery(command);
                sc.Complete();
            }
        }
    }

    static void Main(string[] args)
    {

        DAL dal = new DAL();
        TransactionOptions tso = new TransactionOptions();
        tso.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;

        using (TransactionScope sc = new TransactionScope(TransactionScopeOption.Required,tso))
        {
            dal.FirstMethod();
            dal.SecondMethod();
            sc.Complete();
        }
    }
}

Ответы [ 2 ]

19 голосов
/ 06 апреля 2009

Не думаю, что ваша проблема связана с концепцией .NET TransactionScope. Скорее похоже, что вы описываете ожидаемое поведение транзакций SQL Server. Кроме того, изменение уровня изоляции влияет только на «чтение данных», а не «запись данных». Из SQL Server BOL:

"Выбор уровня изоляции транзакции не влияет на блокировки, полученные для защиты изменений данных. Транзакция всегда получает эксклюзивную блокировку для любых данных, которые она изменяет, и удерживает эту блокировку до завершения транзакции, независимо от уровня изоляции, установленного для этого. транзакция. Для операций чтения уровни изоляции транзакции в первую очередь определяют уровень защиты от последствий изменений, выполненных другими транзакциями. "

Это означает, что вы можете предотвратить поведение блокировки, изменив уровень изоляции для клиента , выполнив оператор SELECT. Уровень изоляции READ COMMITED (по умолчанию) не предотвращает блокировку. Чтобы предотвратить блокировку клиента, вы должны использовать уровень изоляции READ UNCOMMITTED, но вам придется учитывать возможность получения записей, которые были обновлены / вставлены открытой транзакцией (т. Е. Они могут исчезнуть, если транзакция откатывается назад).

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

Хороший вопрос, чтобы поговорить о транзакциях.

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

Вот после первого возврата метода:

First method returns

После возврата второго метода вы добавите еще одну блокировку к таблице.

secont method returns

Если мы выполним оператор select из окна запроса с SPID (55), вы увидите состояние ожидания.

select is waiting

После того, как вы передадите основной метод trans commit, вы получите результат оператора select, и он покажет только shared lock со страницы запроса оператора select.

Scope commits and select returns

X означает эксклюзивную блокировку, IX намеренную блокировку. Вы можете узнать больше из моего блога о транзакциях .

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

...