Entity Framework с NOLOCK - PullRequest
       51

Entity Framework с NOLOCK

132 голосов
/ 29 мая 2009

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

Ответы [ 9 ]

201 голосов
/ 23 сентября 2009

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

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

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;
    }
}
26 голосов
/ 10 апреля 2013

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

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

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

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

19 голосов
/ 05 июня 2014

Хотя я абсолютно согласен с тем, что использование уровня изоляции транзакций Read Uncommitted - лучший выбор, но некоторое время вы вынуждены использовать подсказку 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;
9 голосов
/ 01 июня 2015

Улучшение Доктор Джонс принял ответ и использует PostSharp ;

First " 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 голосов
/ 07 декабря 2011

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

2 голосов
/ 05 апреля 2018

С введением 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
}
2 голосов
/ 30 мая 2009

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

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

Марк

0 голосов
/ 07 января 2016

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...