Entity Framework с NOLOCK

Как я могу использовать функцию NOLOCK в Entity Framework? XML - единственный способ сделать это?


person OneSmartGuy    schedule 29.05.2009    source источник


Ответы (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();
}
person Doctor Jones    schedule 23.09.2009
comment
Отлично @DoctaJonez Было ли что-то новое в EF4 для этого? - person FMFF; 22.02.2012
comment
@FMFF Не знаю, было ли что-то новое в EF4. Я знаю, что приведенный выше код работает с EFv1 и выше. - person Doctor Jones; 28.02.2012
comment
каковы будут последствия? если кто-то опускает transactionScope.Complete () в блоке, упомянутом выше? Как вы думаете, мне следует задать еще один вопрос по этому поводу? - person Eakan Gopalakrishnan; 09.01.2015
comment
@EakanGopalakrishnan. Невозможность вызова этого метода приводит к прерыванию транзакции, поскольку диспетчер транзакций интерпретирует это как системный сбой или исключения, возникшие в рамках транзакции. (Взято из MSDN msdn.microsoft.com/en-us/library/) - person Doctor Jones; 10.01.2015
comment
Да, я нашел ссылку, в которой говорится, что транзакция прерывается. Но я также понял, что если транзакция была сделана только для чтения, то на самом деле это ничему не вредит. Но я не был уверен, есть ли побочные эффекты. Причина, по которой я разместил это в качестве вопроса, заключалась в том, что я честно забыл добавить transactionScope.Complete в свой код, и все же этот материал действительно работал нормально, поскольку я действительно не вносил никаких изменений в БД. Но было интересно, есть ли какой-либо нежелательный эффект на db из-за того, что я не закрыл область видимости. - person Eakan Gopalakrishnan; 11.01.2015
comment
@EakanGopalakrishnan Хорошо, я понимаю. Я бы рекомендовал задать этот вопрос на dba.stackexchange.com. - person Doctor Jones; 12.01.2015
comment
@DoctorJones Вам действительно нужен `transactionScope.Complete ();`? Я привык к большинству связанных с БД областей применения, которые делают подобные вещи автоматически. - person Wjdavis5; 30.01.2017
comment
@ Wjdavis5 да. В документации конкретно говорится, что вы должны: Невозможность вызвать этот метод прерывает транзакцию, потому что диспетчер транзакций интерпретирует это как системный сбой или исключения, возникшие в рамках транзакции. Однако следует также отметить, что вызов этого метода не гарантирует фиксации транзакции. Это просто способ информировать диспетчер транзакций о вашем статусе. См. msdn.microsoft.com/en-us/library/ - person Doctor Jones; 01.02.2017
comment
Это очень медленно. При использовании этого сразу же снизилась производительность, необходимо провести дополнительное расследование, но нельзя использовать его. - person dariogriffo; 17.01.2018
comment
это приводит к NotSupportedException: включение в внешние транзакции не поддерживается. - person JsonStatham; 22.01.2018
comment
@JsonStatham это известная проблема в .Net Core 2.0 github.com/dotnet/corefx/issues/ 24282 - person Doctor Jones; 22.01.2018
comment
@DoctorJones, поэтому чтение незафиксированных невозможно в .Net core 2? - person JsonStatham; 22.01.2018
comment
@JsonStatham он был добавлен в этот запрос на перенос, который предназначен для этапа 2.1.0. - person Doctor Jones; 22.01.2018

Методы расширения могут упростить эту задачу

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;
    }
}
person Alexandre    schedule 31.08.2013
comment
Использование этого в моем проекте приводит к тому, что пул соединений полностью используется, что приводит к исключению. не могу понять почему. У кого-нибудь еще есть эти проблемы? Какие-либо предложения? - person Ben Tidman; 15.01.2014
comment
Нет проблем Бен, не забывай ВСЕГДА избавляться от контекста подключения. - person Alexandre; 16.01.2014
comment
Смог сузить проблему, чтобы исключить область транзакции как возможную причину. Спасибо. Имел какое-то отношение к некоторым повторениям подключения, которые у меня были в моем конструкторе. - person Ben Tidman; 17.01.2014
comment
Я считаю, что область действия должна быть TransactionScopeOption.Suppress - person CodeGrue; 06.05.2014
comment
@Alexandre Что бы произошло, если бы я сделал это в другой транзакции ReadCommitted? Например, я создал транзакцию, чтобы начать сохранять данные, но теперь я запрашиваю больше данных и, следовательно, порожу транзакцию ReadUncommitted внутри? Завершит ли вызов этого завершения мою внешнюю транзакцию? Добрый совет :) - person Jason Loki Smith; 17.02.2017

Если вам нужно что-то в целом, лучший способ, который, как мы обнаружили, менее навязчивый, чем фактический запуск области транзакции каждый раз, - это просто установить уровень изоляции транзакции по умолчанию для вашего соединения после того, как вы создали контекст объекта, выполнив эту простую команду:

this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx

С помощью этого метода мы смогли создать простой поставщик EF, который создает для нас контекст и фактически запускает эту команду каждый раз для всего нашего контекста, так что по умолчанию мы всегда находимся в состоянии «чтение незафиксировано».

person Frank.Germain    schedule 10.04.2013
comment
Сама по себе установка уровня изоляции транзакции не даст никакого эффекта. На самом деле вам нужно работать внутри транзакции, чтобы она имела какой-либо эффект. В документации MSDN для READ UNCOMMITTED указано 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
comment
В документации MSDN говорится: Управляет блокировкой и поведением версий строк инструкций Transact-SQL, выдаваемых подключением к SQL Server. и Указывает, что операторы могут читать строки, которые были изменены другими транзакциями, но еще не зафиксированы. Написанный мной оператор влияет на КАЖДЫЙ оператор SQL, вне зависимости от того, находится он внутри транзакции или нет. Я не люблю противоречить людям в Интернете, но вы явно ошибаетесь в этом, основываясь на том, что мы используем это утверждение в большой производственной среде. Не предполагайте ничего, ПОПРОБУЙТЕ ИХ! - person Frank.Germain; 01.11.2013
comment
Я пробовал их, у нас есть среда с высокой нагрузкой, в которой невыполнение запросов в рамках одной из этих областей транзакции (и соответствующей транзакции) приведет к тупиковой ситуации. Мои наблюдения были сделаны на сервере SQL 2005, поэтому я не знаю, изменилось ли поведение с тех пор. Поэтому я рекомендую это; если вы указали уровень изоляции чтения незафиксированных, но продолжаете испытывать взаимоблокировки, попробуйте поместить свои запросы в транзакцию. Если вы не сталкиваетесь с взаимоблокировками без создания транзакции, этого достаточно. - person Doctor Jones; 01.11.2013
comment
@DoctorJones - что касается Microsoft SQL Server, все запросы по сути являются транзакциями. Указание явной транзакции - это просто средство сгруппировать 2 или более операторов в одну и ту же транзакцию, чтобы их можно было рассматривать как атомарную единицу работы. Команда SET TRANSACTION ISOLATION LEVEL... влияет на свойство уровня соединения и, следовательно, влияет на все операторы SQL, сделанные с этой точки вперед (для THAT-соединения), если она не отменена подсказкой запроса. Такое поведение существует, по крайней мере, с SQL Server 2000 и, вероятно, раньше. - person Solomon Rutzky; 03.11.2014
comment
@DoctorJones - Проверьте: msdn.microsoft.com/en-us/library/ ms173763.aspx. Вот тест. В SSMS откройте запрос (№1) и запустите: 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
comment
+1 В заключение я предложил это как возможное решение другого вопроса вместе с идеей 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;
person Yuriy Rozhovetskiy    schedule 05.06.2014
comment
Мне нравится это решение, хотя я немного изменил регулярное выражение: - person Russ; 10.07.2014
comment
(? ‹TableAlias›] AS [Extent \ d +] (?! WITH (NOLOCK))), чтобы предотвратить добавление nolock в производную таблицу, которое вызывает ошибку. :) - person Russ; 10.07.2014
comment
Установка SuppressNoLock на уровне потока - удобный способ, но легко забыть отключить логическое значение, вы должны использовать функцию, которая возвращает IDisposable, метод Dispose может просто снова установить для логического значения значение false. Кроме того, ThreadStatic не совсем совместим с async / await: stackoverflow.com/questions/13010563/ - person Jaap; 05.08.2015
comment
Или, если вы предпочитаете использовать УРОВЕНЬ ИЗОЛЯЦИИ: 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
comment
Он также добавляет nolock к функциям базы данных. Как избежать для функций? - person Ivan Lewis; 23.08.2017

Улучшение принятого ответа доктора Джонса и использование 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 как таковые.

person myuce    schedule 01.06.2015

Чтобы обойти это, я создаю представление в базе данных и применяю NOLOCK к запросу представления. Затем я рассматриваю представление как таблицу в EF.

person Ryan Galloway    schedule 07.12.2011

С введением 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
}
person Ali    schedule 05.04.2018

Нет, не совсем - Entity Framework - это, по сути, довольно строгий уровень над вашей реальной базой данных. Ваши запросы формулируются на ESQL - Entity SQL - который в первую очередь ориентирован на вашу модель сущностей, а поскольку EF поддерживает несколько бэкэндов базы данных, вы не можете отправлять «родной» SQL прямо в бэкэнд.

Подсказка запроса NOLOCK специфична для SQL Server и не будет работать ни с одной из других поддерживаемых баз данных (если только они не реализовали ту же подсказку, в чем я сильно сомневаюсь).

Марк

person marc_s    schedule 29.05.2009
comment
Этот ответ устарел - вы можете использовать NOLOCK, как упоминали другие, и вы можете выполнять собственный SQL, используя Database.ExecuteSqlCommand() или DbSet<T>.SqlQuery(). - person Dunc; 08.03.2016
comment
@Dunc: спасибо за отрицательный голос - кстати: вам все равно НЕ следует использовать (NOLOCK) - см. Плохие привычки, чтобы исключить - повсюду использовать NOLOCK - НЕ РЕКОМЕНДУЕТСЯ использовать его везде - наоборот! - person marc_s; 08.03.2016

Один из вариантов - использовать хранимую процедуру (аналогичную представлению, предложенному Райаном), а затем выполнить хранимую процедуру из EF. Таким образом, хранимая процедура выполняет грязное чтение, в то время как EF просто передает результаты.

person Rafiki    schedule 07.01.2016