Как я могу использовать функцию NOLOCK в Entity Framework? XML - единственный способ сделать это?
Entity Framework с NOLOCK
Ответы (9)
Нет, но вы можете начать транзакцию и установить уровень изоляции для чтения без фиксации. По сути, это то же самое, что и NOLOCK, но вместо того, чтобы делать это для каждой таблицы, он будет делать это для всего в рамках транзакции.
Если это похоже на то, что вы хотите, вот как вы могли бы это сделать ...
//declare the transaction options
var transactionOptions = new System.Transactions.TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new System.Transactions.TransactionScope(
System.Transactions.TransactionScopeOption.Required,
transactionOptions)
)
//declare our context
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
//don't forget to complete the transaction scope
transactionScope.Complete();
}
Методы расширения могут упростить эту задачу
public static List<T> ToListReadUncommitted<T>(this IQueryable<T> query)
{
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
List<T> toReturn = query.ToList();
scope.Complete();
return toReturn;
}
}
public static int CountReadUncommitted<T>(this IQueryable<T> query)
{
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
int toReturn = query.Count();
scope.Complete();
return toReturn;
}
}
Если вам нужно что-то в целом, лучший способ, который, как мы обнаружили, менее навязчивый, чем фактический запуск области транзакции каждый раз, - это просто установить уровень изоляции транзакции по умолчанию для вашего соединения после того, как вы создали контекст объекта, выполнив эту простую команду:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
С помощью этого метода мы смогли создать простой поставщик EF, который создает для нас контекст и фактически запускает эту команду каждый раз для всего нашего контекста, так что по умолчанию мы всегда находимся в состоянии «чтение незафиксировано».
Transactions running at the READ UNCOMMITTED level do not issue shared locks. Это означает, что вы должны работать в рамках транзакции, чтобы получить выгоду. (взято из msdn.microsoft.com/en-gb/library/ms173763.aspx). Ваш подход может быть менее навязчивым, но он ничего не даст, если вы не используете транзакцию.
- person Doctor Jones; 12.04.2013
SET TRANSACTION ISOLATION LEVEL... влияет на свойство уровня соединения и, следовательно, влияет на все операторы SQL, сделанные с этой точки вперед (для THAT-соединения), если она не отменена подсказкой запроса. Такое поведение существует, по крайней мере, с SQL Server 2000 и, вероятно, раньше.
- person Solomon Rutzky; 03.11.2014
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);. Откройте другой запрос (# 2) и запустите: SELECT * FROM ##Test;. SELECT не вернется, поскольку он блокируется все еще открытой транзакцией на вкладке №1, которая использует эксклюзивную блокировку. Отмените выбор в # 2. Выполните SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED один раз на вкладке №2. Снова запустите только SELECT на вкладке №2, и он вернется. Обязательно запустите ROLLBACK на вкладке №1.
- person Solomon Rutzky; 03.11.2014
TransactionScope (оба указаны в их соответствующих ответах здесь). Я перечислил эту идею первой, так как она была проще и должна была дать желаемый эффект. Пользователь попробовал эту идею, и она сработала. stackoverflow.com/questions/26615535/ < / а>
- person Solomon Rutzky; 04.11.2014
Хотя я абсолютно согласен с тем, что использование уровня изоляции «Чтение незафиксированных транзакций» - лучший выбор, но какое-то время вы заставляли использовать подсказку NOLOCK по запросу менеджера или клиента, и никаких причин против этого не принималось.
С Entity Framework 6 вы можете реализовать собственный DbCommandInterceptor следующим образом:
public class NoLockInterceptor : DbCommandInterceptor
{
private static readonly Regex _tableAliasRegex =
new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
[ThreadStatic]
public static bool SuppressNoLock;
public override void ScalarExecuting(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext)
{
if (!SuppressNoLock)
{
command.CommandText =
_tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
}
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!SuppressNoLock)
{
command.CommandText =
_tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
}
}
}
Имея этот класс на месте, вы можете применить его при запуске приложения:
DbInterception.Add(new NoLockInterceptor());
И условно отключите добавление NOLOCK подсказки в запросы для текущего потока:
NoLockInterceptor.SuppressNoLock = true;
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { if (!SuppressNoLock) command.CommandText = $"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;{Environment.NewLine}{command.CommandText}"; base.ReaderExecuting(command, interceptionContext); }
- person Adi; 12.10.2016
Улучшение принятого ответа доктора Джонса и использование PostSharp;
Первый "ReadUncommitedTransactionScopeAttribute"
[Serializable]
public class ReadUncommitedTransactionScopeAttribute : MethodInterceptionAspect
{
public override void OnInvoke(MethodInterceptionArgs args)
{
//declare the transaction options
var transactionOptions = new TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
//declare our context
using (var scope = new TransactionScope())
{
args.Proceed();
scope.Complete();
}
}
}
}
Тогда, когда вам это понадобится,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Возможность добавить "NOLOCK" с перехватчиком тоже хорошо, но не будет работать при подключении к другим системам баз данных, таким как Oracle как таковые.
Чтобы обойти это, я создаю представление в базе данных и применяю NOLOCK к запросу представления. Затем я рассматриваю представление как таблицу в EF.
С введением EF6 Microsoft рекомендует использовать метод BeginTransaction ().
Вы можете использовать BeginTransaction вместо TransactionScope в EF6 + и EF Core
using (var ctx = new ContractDbContext())
using (var transaction = ctx.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
//any reads we do here will also read uncommitted data
}
Нет, не совсем - Entity Framework - это, по сути, довольно строгий уровень над вашей реальной базой данных. Ваши запросы формулируются на ESQL - Entity SQL - который в первую очередь ориентирован на вашу модель сущностей, а поскольку EF поддерживает несколько бэкэндов базы данных, вы не можете отправлять «родной» SQL прямо в бэкэнд.
Подсказка запроса NOLOCK специфична для SQL Server и не будет работать ни с одной из других поддерживаемых баз данных (если только они не реализовали ту же подсказку, в чем я сильно сомневаюсь).
Марк
Database.ExecuteSqlCommand() или DbSet<T>.SqlQuery().
- person Dunc; 08.03.2016
(NOLOCK) - см. Плохие привычки, чтобы исключить - повсюду использовать NOLOCK - НЕ РЕКОМЕНДУЕТСЯ использовать его везде - наоборот!
- person marc_s; 08.03.2016
Один из вариантов - использовать хранимую процедуру (аналогичную представлению, предложенному Райаном), а затем выполнить хранимую процедуру из EF. Таким образом, хранимая процедура выполняет грязное чтение, в то время как EF просто передает результаты.