Entity Framework с NOLOCK



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

816   9  

9 ответов:

нет, но вы можете начать транзакцию и установить уровень изоляции для чтения uncommited. Это по существу делает то же самое, что и 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;
    }
}

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

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

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

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

хотя я абсолютно согласен, что использование Read Uncommitted transaction isolation level является лучшим выбором, но некоторое время вы вынуждены использовать подсказку 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;

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

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

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

Марк

С введением EF6 Корпорация Майкрософт рекомендует использовать метод 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
}

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

Comments

    Ничего не найдено.