Каков наилучший способ реализовать разные уровни изоляции для отдельных транзакций при использовании клиентской среды, ORM или аналогичных запросов на сборку, которая не поддерживает подсказки запросов, такие как WITH (NOLOCK)?
Представьте приложение, которое использует уровень ReadUncommitted для ряда сложных и длинных запросов (хорошо осведомленных о связанных рисках), и предполагается, что он должен работать с NHibernate и критериями запроса (или QueryOver/LINQ, просто нет конкатенации строк!).
NHibernate не поддерживает подсказку with (nolock) (за исключением случаев использования встроенного SQL, который в настоящее время используется во многих случаях).
Итак, чтобы заменить собственные строки SQL и их утомительный код здания, я хочу использовать транзакции с IsolationLevel.ReadUnCommitted для замены 'with (nolock)'.
Но соединение остается на измененном уровне изоляции даже после Commit/Rollback, запуская все на новом уровне. Даже после connection.Close(), он возвращается в пул соединений и повторно используется с измененным уровнем изоляции.
Я изначально заметил это, потому что я протестировал открытие соединения с уровнем изоляции Snapshot и отправку простого запроса, чтобы отключить Read Uncommitted, если включен режим моментального снимка в базе данных (вообще нет простого переключения на моментальный снимок). В тестовой базе данных отключен режим моментального снимка, поэтому я получил исключение и установил переменную UseReadUncommitted в значение "true" в блоке catch, но более поздние запросы из "нового" /повторно используемого соединения все равно получили одно и то же исключение.
Я написал простой класс для переноса обработки транзакций в используемом блоке, автоматически сбросив значение IsolationLevel в .Dispose(). Но это, по-видимому, вызывает два дополнительных обращения к БД, и я не уверен, может ли измененный уровень изоляции "выжить" в определенных ситуациях и повлиять на другие запросы. Код работал в первую очередь, он для простых соединений/транзакций ADO.NET(я буду делать еще один сеанс NHibernate, если это хорошо!).
Любые предложения?
public class TransactionContainerTempIsolationLevel : IDisposable
{
public IsolationLevel OldIsolationLevel { get; private set; }
public IsolationLevel TempIsolationLevel { get; private set; }
public IDbTransaction Transaction { get; private set; }
private readonly IDbConnection _conn;
public TransactionContainerTempIsolationLevel(IDbConnection connection, IsolationLevel tempIsolationLevel)
{
_conn = connection;
LocalIsolationLevel = localIsolationLevel;
var checkTran = _conn.BeginTransaction();
if (checkTran.IsolationLevel == tempIsolationLevel)
{
Transaction = checkTran;
}
else
{
OldIsolationLevel = checkTran.IsolationLevel;
checkTran.Dispose();
Transaction = _conn.BeginTransaction(tempIsolationLevel);
}
}
public void Dispose()
{
Transaction.Dispose();
if (OldIsolationLevel != TempIsolationLevel)
{
using (var restoreTran = _conn.BeginTransaction(OldIsolationLevel))
{
restoreTran.Commit();
}
}
}
}