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