Как показано предыдущими вопросами о переполнении стека ( > TransactionScope и Connection Pooling и Как 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!
