Get Entity Framework 6 usa NOLOCK em suas instruções SELECT

Eu estou usando o Entity Framework 6 em um projeto MVC 5. Como você sabe, as consultas SELECT no SQL Server são mais rápidas e eficientes se usarmos o WITH (NOLOCK) nelas. Eu verifiquei algumas instruções SQL SELECT geradas pelo Entity Framework 6 e percebi que nenhuma delas contém NOLOCK.

Eu não quero usar transactions em minhas operações de busca para ler transactions não confirmadas.

Como posso impor o EF 6 para usar o NOLOCK nas instruções SELECT geradas abaixo?

Primeiro de tudo … Você nunca deve usar NOLOCK para cada instrução SQL. Isso poderia comprometer a integridade de seus dados.

É como qualquer outra sugestão, um mecanismo que você deve usar apenas quando você faz algo fora do comum.

Não há como informar ao provedor EF para processar a dica NoLock. Se você realmente precisa ler dados não confirmados, você tem a seguinte opção.

  1. Escreva seu próprio EntityFramework Provider.

  2. Use um Interceptador de Comando para modificar a instrução antes de ser executada. http://msdn.microsoft.com/pt-br/data/dn469464.aspx

  3. Use um TransactionScope com IsolationLevel.ReadUncommited.

Eu sei que você disse que não quer usar Transações, mas é a única maneira pronta para ler dados não confirmados. Também não produz muita sobrecarga, pois cada instrução no SQL Server “implicitamente” é executada em uma transação.

 using (new TransactionScope( TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted })) { using (var db = new MyDbContext()) { // query } } 

EDIT: É importante notar também que NOLOCK para atualizações e exclusões (selects permanecem intactos) foi preterido pela Microsoft a partir do SQL Server 2016 e e será removido em ‘um’ lançamento futuro.

https://docs.microsoft.com/pt-br/sql/database-engine/deprecated-database-engine-features-in-sql-server-2016?view=sql-server-2017

Eu concordo com o que o codeworx diz de uma maneira que Read Uncommitted pode ser perigoso. Se você pode viver com leituras sujas, vá em frente.

Eu encontrei uma maneira de fazer isso funcionar sem alterar nada nas consultas atuais.

Você precisa criar um DbCommandInterceptor assim:

 public class IsolationLevelInterceptor : DbCommandInterceptor { private IsolationLevel _isolationLevel; public IsolationLevelInterceptor(IsolationLevel level) { _isolationLevel = level; } //[ThreadStatic] //private DbCommand _command; public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { SetTransaction(command); } public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { SetTransaction(command); } public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { SetTransaction(command); } private void SetTransaction(DbCommand command) { if (command != null) { if (command.Transaction == null) { var t = command.Connection.BeginTransaction(_isolationLevel); command.Transaction = t; //_command = command; } } } } 

então, no cctor (construtor estático do seu dbcontext), basta adicionar o interceptador à coleção DbInfrastructure of entity framework.

 DbInterception.Add(new IsolationLevelInterceptor()); 

Isto irá para cada comando que a EF envia para a loja, envolve uma transação com esse Nível de Isolamento.

No meu caso funcionou bem, uma vez que escrevemos dados através de uma API onde esses dados não são baseados nas leituras do database. (dados podem ser corrompidos por causa das leituras sujas) e assim funcionou bem.

Você pode usar uma solução alternativa que não use escopos de transação para cada consulta. Se você executar o código abaixo, o ef usará o mesmo nível de isolamento de transação para o mesmo ID do processo do servidor. Como o ID do processo do servidor não é alterado na mesma solicitação, apenas uma chamada para cada solicitação é suficiente. Isso também funciona no EF Core.

 this.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");