SQL Server: утечка уровня изоляции через соединения в пуле

Как показали предыдущие вопросы о переполнении стека (TransactionScope и пул соединений а такжеКак SqlConnection управляет IsolationLevel?), уровень изоляции транзакций просачивается через пул соединений с SQL Server и ADO.NET (также System.Transactions и EF, потому что они основаны на ADO.NET).

Это означает, что в любом приложении может произойти следующая опасная последовательность событий:

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

Вопрос:Как лучше всего предотвратить этот сценарий? Действительно ли сейчас необходимо везде использовать явные транзакции?

Вот автономное воспроизведение. Вы увидите, что третий запрос унаследовал уровень Serializable от второго запроса.

class Program
{
    static void Main(string[] args)
    {
        RunTest(null);
        RunTest(IsolationLevel.Serializable);
        RunTest(null);
        Console.ReadKey();
    }

    static void RunTest(IsolationLevel? isolationLevel)
    {
        using (var tran = isolationLevel == null ? null : new TransactionScope(0, new TransactionOptions() { IsolationLevel = isolationLevel.Value }))
        using (var conn = new SqlConnection("Data Source=(local); Integrated Security=true; Initial Catalog=master;"))
        {
            conn.Open();

            var cmd = new SqlCommand(@"
select         
        case transaction_isolation_level 
            WHEN 0 THEN 'Unspecified' 
            WHEN 1 THEN 'ReadUncommitted' 
            WHEN 2 THEN 'ReadCommitted' 
            WHEN 3 THEN 'RepeatableRead' 
            WHEN 4 THEN 'Serializable' 
            WHEN 5 THEN 'Snapshot' 
        end as lvl, @@SPID
     from sys.dm_exec_sessions 
    where session_id = @@SPID", conn);

            using (var reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    Console.WriteLine("Isolation Level = " + reader.GetValue(0) + ", SPID = " + reader.GetValue(1));
                }
            }

            if (tran != null) tran.Complete();
        }
    }
}

Выход:

Isolation Level = ReadCommitted, SPID = 51
Isolation Level = Serializable, SPID = 51
Isolation Level = Serializable, SPID = 51 //leaked!

Ответы на вопрос(3)

Ваш ответ на вопрос