Как глобально изменить IsolationLevel всех транзакций Entity Framework
Я оцениваю EF для моих следующих новых приложений.
Как я могу глобально изменить IsolationLevel всех транзакций EF в приложении? Пример: предположим, я хочу использовать "Read Committed Snapshot".
Хотя можно указать IsolationLevel, когда мне все равно явно нужен TransactionScope (см. Код ниже), было бы ужасно инкапсулировать каждую операцию сохранения EF в TransactionScope.
'OK
Using tsc As New TransactionScope(TransactionScopeOption.RequiresNew, TransactionOption.ReadCommitted)
UpdateShoppingCart
EnqueueNewOrder
SendConfirmationEmail
tsc.Complete
End Using
'Is this really the only way to avoid Serializable?
Using tsc As New TransactionScope(TransactionScopeOption.RequiresNew, TransactionOption.ReadCommitted)
_ctx.SaveChanges()
tsc.Complete
End Using
Class TransactionOption
Public Shared ReadOnly ReadCommitted As New TransactionOptions() With {
.IsolationLevel = IsolationLevel.ReadCommitted,
.Timeout = TransactionManager.DefaultTimeout
}
End Class
Я предполагаю, что смешивание IsolationLevles не очень хорошая идея. Я не прав с этим?
С Serializable и SQL Server (в отличие от Oracle) вставка простого невинно выглядящего чтения может вызвать тупиковую блокировку преобразования.
Из EF FAQ:"Рекомендуется использовать транзакции READ COMMITTED и использовать READ COMMITTED SNAPSHOT ISOLATION, если вам нужно, чтобы читатели не блокировали писателей, а писатели не блокировали читателей".
Я не понимаю, почему EF по умолчанию настроен на Serializable и так трудно изменить уровень изоляции по умолчанию - в SQL Server (в отличие от Oracle с несколькими версиями) по умолчанию используется пессимистическая модель параллелизма. Вариант конфигурации должен быть очень простым для реализации - или я что-то здесь упускаю?
2 ответа
Я почти уверен, что уровень изоляции транзакции EF по умолчанию основан на используемом поставщике базы данных. SaveChanges
выполняет этот код:
...
try
{
this.EnsureConnection();
flag = true;
Transaction current = Transaction.Current;
bool flag2 = false;
if (connection.CurrentTransaction == null)
{
flag2 = null == this._lastTransaction;
}
using (DbTransaction transaction = null)
{
if (flag2)
{
transaction = connection.BeginTransaction();
}
objectStateEntriesCount = this._adapter.Update(this.ObjectStateManager);
if (transaction != null)
{
transaction.Commit();
}
}
}
...
Как вы видете BeginTransaction
называется без IsolationLevel
указано. Под капотом создается специфическая для провайдера транзакция с IsolationLevel.Unspecified
, Неуказанный уровень изоляции должен приводить к уровню изоляции по умолчанию для сервера / драйвера базы данных. В SQL Server по умолчанию уровень изоляции READ COMMITED
поэтому я ожидаю, что он должен использовать это, но я еще не проверял это.
Если вы хотите глобально изменить уровень изоляции, вы можете переопределить SaveChanges
в вашем классе происходит от ObjectContext
и завернуть base.SaveChanges()
на заказ TransactionScope
,
В VB может быть ограничение, но в C# вы бы сделали это так:
TransactionOptions transactionOptions = GetTransactionOptions();
new TransactionScope(TransactionScopeOption.RequiresNew, transactionOptions)
Обратите внимание, что вторым параметром является actionOptions, его можно создать следующим образом:
public TransactionOptions GetTransactionOptions()
{
TransactionOptions transactionOptions = new TransactionOptions();
transactionOptions.IsolationLevel = IsolationLevel.ReadCommitted;
return transactionOptions;
}
Поместив приведенный выше код в метод Factory и вызывая этот метод factory всякий раз, когда вам нужны параметры транзакции, вы можете иметь одно место для изменения всех областей транзакции в своем решении.