Trabalhando em alguns sistemas, nos foi recomendado, pelo próprio cliente, que fizéssemos uso do operador NOLOCK em todos os comandos de consulta que fizéssemos no banco de dados. Isso parece uma atitude exagerada, mas por experiência do cliente, com receio de deadlocks, tínhamos de seguir à risca essa indicação.
O SQL Server utiliza recursos de bloqueio (tanto para escrita quanto leitura) para garantir a integridade dos dados. Enquanto executamos uma transação seguimos o conceito ACID (se você não conhece este conceito, veja este post Entity Framework 6 – Database.BeginTransaction() e Database.UseTransaction(DbTransaction)). Por conta disso, dentro de uma transação qualquer comando paralelo que tente acessar os dados com os quais estamos trabalhando entram nesse bloqueio, e assim são forçados a aguardar o término de nossa transação.
O operador NOLOCK ignora esse bloqueio e acessa os dados de qualquer forma, mesmo que alguma transação esteja ocorrendo. O NOLOCK permite a leitura de dados não commitados. Outra forma de obter o mesmo resultado é executando a linha de comando abaixo, assim alterando o nível de isolamento da transação em execução:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
O ponto negativo do uso de NOLOCK ou nível de isolamento READ UNCOMMITED é que eles podem ocasionar em leituras sujas (dirty reads). Leitura suja é o cenário no qual lemos dados que ainda não foram commitados no banco de dados.
Ler dados não commitados não é nada bom, pois nada impede que esses dados sejam revertidos (por meio de um comando ROLLBACK) e nos deixem “dados sujos nas mãos”.
Por isso ressalvo que é preciso estudar a utilização de cada comando, cada parâmetro e cada operador de qualquer linguagem, banco de dados ou tecnologia, para assim verificar se ele é aceitável ou não em sua aplicação.
Geralmente esse é o sentimento quando encontramos esse tipo de cenário…
O Entity Framework não faz uso do operador NOLOCK, mas suporta a alteração do nível de isolamento para READ UNCOMMITED.
Com o Entity Framework 6, nós podemos fazer uso do método BeginTransaction(), e construir algo como o bloco de código abaixo:
using (var context = new ProgramContext()) { using (var tran = context.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted)) { var query = from q1 in context.FlightAttendants where q1.Company != null && q1.Company.Name == "aa" select q1; var result = query.ToList(); tran.Commit(); } }
Nas versões anteriores ao Entity Framework 6 isso não é possível, pois o método BeginTransaction() não existe. Mas podemos fazer isso de outras duas formas. A primeira é utilizando a classe TransactionScope:
var transactionOptions = new TransactionOptions(); transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted; using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions)) { using (var context = new ProgramContext()) { var query = from q1 in context.FlightAttendants where q1.Company != null && q1.Company.Name == "aa" select q1; var result = query.ToList(); } }
E a segunda forma é rodando o comando “SET TRANSACTION ISOLATION LEVEL” com a instância ativa do contexto, como apresentado abaixo:
using (var context = new ProgramContext()) { context.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED"); var query = from q1 in context.FlightAttendants where q1.Company != null && q1.Company.Name == "aa" select q1; var result = query.ToList(); }
Espero que seja útil.
Por
MSc. Fernando Henrique Inocêncio Borba Ferreira
Microsoft Most Valuable Professional – Visual C#
Referências:
http://www.hanselman.com/blog/GettingLINQToSQLAndLINQToEntitiesToUseNOLOCK.aspx
Muito bom o artigo. Sempre tive dúvidas a respeito do NOLOCK.
Oi Fabio,
Tudo bem?
Ficou feliz que agora está mais claro. Obrigado pelo comentário.
[]s!
Tanto de artigos em português, como em inglês essa foi a explicação mais clara que já vi sobre isso,
Parabéns! Excelente didática.
ps: imagem legal kkk
Oi Afonso. Obrigado pelo comentário. []s!