Come posso usare la NOLOCKfunzione su Entity Framework? XML è l'unico modo per farlo?
Come posso usare la NOLOCKfunzione su Entity Framework? XML è l'unico modo per farlo?
Risposte:
No, ma è possibile avviare una transazione e impostare il livello di isolamento per leggere senza commit . Questo essenzialmente fa lo stesso di NOLOCK, ma invece di farlo in base alla tabella, lo farà per tutto ciò che rientra nell'ambito della transazione.
Se questo suona come quello che vuoi, ecco come potresti fare per farlo ...
//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();
}
I metodi di estensione possono semplificarlo
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;
}
}
Se hai bisogno di qualcosa in generale, il modo migliore che abbiamo trovato meno invadente rispetto all'avvio effettivo di un ambito di transazione ogni volta, è semplicemente impostare il livello di isolamento della transazione predefinito sulla tua connessione dopo aver creato il contesto dell'oggetto eseguendo questo semplice comando:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
Con questa tecnica, siamo stati in grado di creare un semplice provider EF che crea il contesto per noi ed esegue effettivamente questo comando ogni volta per tutto il nostro contesto in modo da essere sempre "letti non confermati" per impostazione predefinita.
Transactions running at the READ UNCOMMITTED level do not issue shared locks. Ciò implica che è necessario eseguire una transazione per ottenere il vantaggio. (tratto da msdn.microsoft.com/en-gb/library/ms173763.aspx ). Il tuo approccio potrebbe essere meno invadente, ma non otterrà nulla se non utilizzi una transazione.
SET TRANSACTION ISOLATION LEVEL...comando influenza una proprietà a livello di connessione e quindi influenza tutte le istruzioni SQL fatte da quel punto in avanti (per quella connessione), a meno che non vengano sovrascritte da un suggerimento per la query. Questo comportamento esiste da almeno SQL Server 2000 e probabilmente prima.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);. Aprire un'altra query (# 2) ed eseguire: SELECT * FROM ##Test;. SELECT non verrà restituito poiché viene bloccato dalla transazione ancora aperta nella scheda n. 1 che utilizza un blocco esclusivo. Annulla SELECT in # 2. Esegui SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDuna volta nella scheda # 2. Esegui nuovamente SELEZIONA nella scheda # 2 e tornerà. Assicurati di eseguire ROLLBACKnella scheda # 1.
Anche se sono assolutamente d'accordo sul fatto che l'utilizzo del livello di isolamento della transazione Read Uncommitted sia la scelta migliore, ma qualche volta hai costretto a utilizzare il suggerimento NOLOCK su richiesta del gestore o del cliente e non sono state accettate motivazioni.
Con Entity Framework 6 è possibile implementare il proprio DbCommandInterceptor in questo modo:
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)");
}
}
}
Con questa classe attiva, puoi applicarla all'avvio dell'applicazione:
DbInterception.Add(new NoLockInterceptor());
E disattivare condizionalmente l'aggiunta di NOLOCKsuggerimenti nelle query per il thread corrente:
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); }
Miglioramento della risposta accettata del dottor Jones e utilizzo di PostSharp ;
Primo " 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();
}
}
}
}
Quindi ogni volta che ne hai bisogno,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Essere in grado di aggiungere "NOLOCK" con un intercettore è anche bello, ma non funzionerà quando ci si collega ad altri sistemi di database come Oracle in quanto tale.
Per ovviare a questo, creo una vista sul database e applico NOLOCK sulla query della vista. Tratto quindi la vista come una tabella all'interno di EF.
Con l'introduzione di EF6, Microsoft consiglia di utilizzare il metodo BeginTransaction ().
È possibile utilizzare BeginTransaction anziché TransactionScope in EF6 + e 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
}
No, non proprio - Entity Framework è fondamentalmente un livello abbastanza rigoroso sopra il tuo database reale. Le tue query sono formulate in ESQL - Entity SQL - che è prima di tutto mirato al tuo modello di entità e poiché EF supporta più back-end di database, non puoi realmente inviare SQL "nativo" direttamente al tuo back-end.
Il suggerimento per le query NOLOCK è una cosa specifica di SQL Server e non funzionerà su nessuno degli altri database supportati (a meno che non abbiano implementato lo stesso suggerimento - di cui dubito fortemente).
Marc
Database.ExecuteSqlCommand()o DbSet<T>.SqlQuery().
(NOLOCK)comunque - vedi Cattive abitudini per calciare - mettere NOLOCK ovunque - NON È CONSIGLIATO usare questo ovunque - al contrario!