Як я можу використовувати NOLOCKфункцію на Entity Framework? Чи єдиний спосіб зробити це XML?
Як я можу використовувати NOLOCKфункцію на Entity Framework? Чи єдиний спосіб зробити це XML?
Відповіді:
Ні, але ви можете розпочати транзакцію і встановити рівень ізоляції, щоб читати її не можна . Це по суті робить те саме, що і 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;
}
}
Якщо вам потрібно щось взагалі, найкращий спосіб, який ми виявили, що менш нав'язливий, ніж щоразу починати транзакцію, - це просто встановити рівень ізоляції транзакцій за вашим з'єднанням після того, як ви створили свій об'єктний контекст, виконавши цю просту команду:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
За допомогою цієї методики нам вдалося створити простий провайдер EF, який створює для нас контекст і фактично виконує цю команду кожного разу для всього нашого контексту, щоб ми за замовчуванням завжди знаходились у режимі "читання не передано".
Transactions running at the READ UNCOMMITTED level do not issue shared locks. Це означає, що для отримання вигоди ви повинні працювати в рамках транзакції. (взято з msdn.microsoft.com/en-gb/library/ms173763.aspx ). Ваш підхід може бути менш нав'язливим, але він не досягне нічого, якщо не використовувати транзакцію.
SET TRANSACTION ISOLATION LEVEL...Команда впливає на властивості з'єднання рівня і , отже , впливає на всі оператори SQL , зроблені з цього моменту (для даного з'єднання), якщо вони НЕ були перезаписані натяком запиту. Така поведінка існувала як мінімум з SQL Server 2000, і, швидше за все, раніше.
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. Запустіть просто ВИБІР знову на вкладці №2, і вона повернеться. Обов’язково запустіть ROLLBACKвкладку №1.
Хоча я абсолютно погодився, що найкращим вибором є рівень ізоляції транзакцій 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;
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); }
Вдосконалення прийнятої відповіді доктора Джонса та використання 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.
З впровадженням 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
}
Ні, не дуже - Entity Framework - це в основному досить суворий рівень над вашою фактичною базою даних. Ваші запити сформульовані в ESQL - Entity SQL, який, перш за все, орієнтований на модель вашої сутності, і оскільки EF підтримує декілька резервних копій баз даних, ви не можете реально надсилати "рідний" SQL безпосередньо у свій бекенд.
Підказка щодо запитів NOLOCK - специфічна річ для SQL Server, і вона не працюватиме на будь-якій іншій підтримуваній базі даних (якщо тільки вони також не реалізували ту саму підказку - в чому я сильно сумніваюся).
Марк
Database.ExecuteSqlCommand()або DbSet<T>.SqlQuery().
(NOLOCK)будь-якому випадку - див Шкідливі звички до гри - покласти NOLOCK всюди - це НЕ рекомендується використовувати це всюди - зовсім навпаки!