Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Entity Framework с NOLOCK

Автор вопроса: OneSmartGuyОтветов: 9

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

entity-framework

Ответы

Ответ 1 · рейтинг 211 · Doctor Jones

Нет, но вы можете начать транзакцию и установить уровень изоляции для чтения без фиксации. По сути, это то же самое, что и 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();
}

Ответ 2 · рейтинг 86 · Alexandre

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

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;
    }
}

Ответ 3 · рейтинг 28 · Frank.Germain

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

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

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

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

Ответ 4 · рейтинг 22 · Yuriy Rozhovetskiy

Хотя я абсолютно согласен с тем, что использование уровня изоляции "Чтение незафиксированных транзакций" - лучший выбор, но какое-то время вы заставляли использовать подсказку 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;

Ответ 5 · рейтинг 10 · myuce

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

Ответ 6 · рейтинг 7 · Ryan Galloway

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

Ответ 7 · рейтинг 5 · Ali

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

Ответ 8 · рейтинг 2 · marc_s

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

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

Марк

Ответ 9 · рейтинг 1 · Rafiki

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

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы