Entity Framework с NOLOCK

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

Ответ 1

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

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

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

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

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

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

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

Ответ 4

Хотя я абсолютно согласен с тем, что использование режима изоляции 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;

Ответ 5

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

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

Ответ 7

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

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

Марк

Ответ 8

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

Ответ 9

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