Como posso usar a NOLOCK
função no Entity Framework? XML é a única maneira de fazer isso?
Como posso usar a NOLOCK
função no Entity Framework? XML é a única maneira de fazer isso?
Respostas:
Não, mas você pode iniciar uma transação e definir o nível de isolamento para ler descomprometido . Isso basicamente faz o mesmo que o NOLOCK, mas, em vez de fazê-lo por tabela, o fará para tudo dentro do escopo da transação.
Se isso soa como o que você deseja, veja como você pode fazê-lo ...
//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();
}
Os métodos de extensão podem facilitar isso
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 você precisar de algo em geral, a melhor maneira que descobrimos ser menos invasiva do que realmente iniciar um escopo de transações a cada vez é simplesmente definir o nível de isolamento de transação padrão em sua conexão depois de criar o contexto do objeto executando este comando simples:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/en-us/library/aa259216(v=sql.80).aspx
Com essa técnica, fomos capazes de criar um provedor EF simples que cria o contexto para nós e, na verdade, executa esse comando todas as vezes para todo o nosso contexto, de modo que estamos sempre na "leitura não confirmada" por padrão.
Transactions running at the READ UNCOMMITTED level do not issue shared locks
. Isso implica que você deve estar executando dentro de uma transação para obter o benefício. (extraído de msdn.microsoft.com/en-gb/library/ms173763.aspx ). Sua abordagem pode ser menos intrusiva, mas não conseguirá nada se você não usar uma transação.
SET TRANSACTION ISOLATION LEVEL...
comando afeta uma propriedade no nível da conexão e, portanto, afeta todas as instruções SQL feitas desse ponto em diante (para essa conexão), a menos que sejam substituídas por uma dica de consulta. Esse comportamento existe desde pelo menos o SQL Server 2000 e provavelmente antes.
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
. Abra outra consulta (# 2) e execute: SELECT * FROM ##Test;
. O SELECT não retornará, pois está sendo bloqueado pela transação ainda aberta na guia nº 1, que está usando um bloqueio exclusivo. Cancele o SELECT no # 2. Execute SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
uma vez na guia nº 2. Execute apenas o SELECT novamente na guia nº 2 e ele voltará. Certifique-se de executar ROLLBACK
na guia nº 1.
Embora eu tenha concordado absolutamente que usar o nível de isolamento de transação Read Uncommitted é a melhor opção, mas algum tempo você forçou a usar a dica NOLOCK por solicitação do gerente ou cliente e nenhuma razão contra isso foi aceita.
Com o Entity Framework 6, você pode implementar o próprio DbCommandInterceptor assim:
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)");
}
}
}
Com esta classe, você pode aplicá-la no início do aplicativo:
DbInterception.Add(new NoLockInterceptor());
E desabilite condicionalmente a adição de NOLOCK
dica nas consultas do encadeamento atual:
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); }
Aprimorar a resposta aceita pelo doutor Jones e usar o PostSharp ;
Primeiro " 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();
}
}
}
}
Então, sempre que você precisar,
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
Ser capaz de adicionar "NOLOCK" a um interceptador também é bom, mas não funciona ao conectar-se a outros sistemas de banco de dados como o Oracle.
Para contornar isso, crio uma visualização no banco de dados e aplico NOLOCK na consulta da visualização. Trato então a visualização como uma tabela na EF.
Com a introdução do EF6, a Microsoft recomenda o uso do método BeginTransaction ().
Você pode usar BeginTransaction em vez de TransactionScope no 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
}
Não, na verdade - o Entity Framework é basicamente uma camada bastante rígida acima do banco de dados real. Suas consultas são formuladas no ESQL - SQL de entidade - que é primeiramente direcionado ao seu modelo de entidade e, como o EF suporta vários back-ends de banco de dados, você não pode realmente enviar SQL "nativo" diretamente para seu back-end.
A dica de consulta NOLOCK é uma coisa específica do SQL Server e não funcionará em nenhum dos outros bancos de dados suportados (a menos que eles também tenham implementado a mesma dica - o que duvido muito).
Marc
Database.ExecuteSqlCommand()
ou DbSet<T>.SqlQuery()
.
(NOLOCK)
mesmo assim - veja Bad Habits to kick - colocando NOLOCK em todos os lugares - NÃO É RECOMENDADO usar isso em todos os lugares - muito pelo contrário!