¿Cómo puedo usar la NOLOCK
función en Entity Framework? ¿Es XML la única forma de hacer esto?
¿Cómo puedo usar la NOLOCK
función en Entity Framework? ¿Es XML la única forma de hacer esto?
Respuestas:
No, pero puede iniciar una transacción y establecer el nivel de aislamiento para leer sin confirmar . Básicamente, esto hace lo mismo que NOLOCK, pero en lugar de hacerlo por tabla, lo hará para todo lo que esté dentro del alcance de la transacción.
Si eso suena como lo que quieres, así es como puedes hacerlo ...
//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();
}
Los métodos de extensión pueden facilitar esto
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;
}
}
Si necesita algo en general, la mejor manera que encontramos de que es menos intrusivo que iniciar un alcance de transacciones cada vez, es simplemente establecer el nivel de aislamiento de transacción predeterminado en su conexión después de haber creado su contexto de objeto ejecutando este comando simple:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
Con esta técnica, pudimos crear un proveedor de EF simple que crea el contexto para nosotros y realmente ejecuta este comando cada vez para todo nuestro contexto para que siempre estemos en "lectura no comprometida" de forma predeterminada.
Transactions running at the READ UNCOMMITTED level do not issue shared locks
. Esto implica que debe estar ejecutándose dentro de una transacción para obtener el beneficio. (tomado de msdn.microsoft.com/en-gb/library/ms173763.aspx ). Su enfoque puede ser menos intrusivo, pero no logrará nada si no utiliza una transacción.
SET TRANSACTION ISOLATION LEVEL...
comando afecta a una propiedad de nivel de conexión y, por lo tanto, afecta a todas las declaraciones SQL realizadas desde ese punto en adelante (para ESA conexión), a menos que se anule una sugerencia de consulta. Este comportamiento ha existido desde al menos SQL Server 2000, y probablemente antes.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
. Abrir otra consulta (# 2) y de ejecución: SELECT * FROM ##Test;
. SELECT no regresará ya que está siendo bloqueado por la transacción aún abierta en la pestaña # 1 que está usando un bloqueo exclusivo. Cancele SELECCIONAR en el n. ° 2. Ejecutar SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
una vez en la pestaña # 2. Ejecute nuevamente SELECT en la pestaña # 2 y volverá. Asegúrese de ejecutar ROLLBACK
en la pestaña # 1.
Aunque estuve absolutamente de acuerdo en que usar el nivel de aislamiento de transacciones no confirmadas de lectura es la mejor opción, pero en algún momento se obligó a usar la sugerencia de NOLOCK a pedido del gerente o cliente y no se aceptaron razones en contra de esto.
Con Entity Framework 6 puede implementar su propio DbCommandInterceptor de esta manera:
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 esta clase en su lugar, puede aplicarla al inicio de la aplicación:
DbInterception.Add(new NoLockInterceptor());
Y apague condicionalmente la adición de NOLOCK
pistas en las consultas para el hilo actual:
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); }
Mejorar la respuesta aceptada del Doctor Jones y usar PostSharp ;
Primero " 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();
}
}
}
}
Entonces, cuando lo necesites,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Ser capaz de agregar "NOLOCK" con un interceptor también es bueno, pero no funcionará cuando se conecte a otros sistemas de bases de datos como Oracle como tal.
Para evitar esto, creo una vista en la base de datos y aplico NOLOCK en la consulta de la vista. Luego trato la vista como una tabla dentro de EF.
Con la introducción de EF6, Microsoft recomienda usar el método BeginTransaction ().
Puede usar BeginTransaction en lugar de TransactionScope en EF6 + y 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, no realmente: Entity Framework es básicamente una capa bastante estricta sobre su base de datos real. Sus consultas están formuladas en ESQL (Entity SQL), que está dirigido en primer lugar a su modelo de entidad, y dado que EF admite múltiples backends de base de datos, realmente no puede enviar SQL "nativo" directamente a su backend.
La sugerencia de consulta NOLOCK es una cosa específica de SQL Server y no funcionará en ninguna de las otras bases de datos compatibles (a menos que también hayan implementado la misma sugerencia, lo cual dudo mucho).
Bagazo
Database.ExecuteSqlCommand()
o DbSet<T>.SqlQuery()
.
(NOLOCK)
todos modos - mira Bad Habits para patear - poniendo NOLOCK en todas partes - NO SE RECOMIENDA usar esto en todas partes - ¡todo lo contrario!